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.
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
38 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
39 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
41 void Mod_BrushInit(void)
43 // Cvar_RegisterVariable(&r_subdivide_size);
44 Cvar_RegisterVariable(&halflifebsp);
45 Cvar_RegisterVariable(&r_novis);
46 Cvar_RegisterVariable(&r_miplightmaps);
47 Cvar_RegisterVariable(&r_lightmaprgba);
48 Cvar_RegisterVariable(&r_nosurftextures);
49 Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
50 Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
51 Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
52 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
62 Mod_CheckLoaded(model);
64 // LordHavoc: modified to start at first clip node,
65 // in other words: first node of the (sub)model
66 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
67 while (node->contents == 0)
68 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
70 return (mleaf_t *)node;
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
77 leaf = Mod_Q1BSP_PointInLeaf(model, p);
80 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
83 memcpy(out, leaf->ambient_sound_level, i);
89 memset(out, 0, outsize);
93 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)
97 if (node->contents < 0)
100 if (node->contents == CONTENTS_SOLID)
102 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
106 // node - recurse down the BSP tree
107 switch (BoxOnPlaneSide(mins, maxs, node->plane))
110 node = node->children[0];
113 node = node->children[1];
116 if (node->children[0]->contents != CONTENTS_SOLID)
117 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
119 node = node->children[1];
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
128 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
137 return CONTENTS_EMPTY;
139 Mod_CheckLoaded(model);
141 // LordHavoc: modified to start at first clip node,
142 // in other words: first node of the (sub)model
143 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
144 while (node->contents == 0)
145 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
147 return ((mleaf_t *)node)->contents;
151 typedef struct findnonsolidlocationinfo_s
159 findnonsolidlocationinfo_t;
162 extern cvar_t samelevel;
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
166 int i, surfnum, k, *tri, *mark;
167 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
173 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
175 surf = info->model->brushq1.surfaces + *mark;
176 if (surf->flags & SURF_SOLIDCLIP)
179 VectorCopy(surf->plane->normal, surfnormal);
180 if (surf->flags & SURF_PLANEBACK)
181 VectorNegate(surfnormal, surfnormal);
183 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
185 for (k = 0;k < mesh->numtriangles;k++)
187 tri = mesh->element3i + k * 3;
188 VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
189 VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
190 VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
191 VectorSubtract(vert[1], vert[0], edge[0]);
192 VectorSubtract(vert[2], vert[1], edge[1]);
193 CrossProduct(edge[1], edge[0], facenormal);
194 if (facenormal[0] || facenormal[1] || facenormal[2])
196 VectorNormalize(facenormal);
198 if (VectorDistance(facenormal, surfnormal) > 0.01f)
199 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
201 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
202 if (f <= info->bestdist && f >= -info->bestdist)
204 VectorSubtract(vert[0], vert[2], edge[2]);
205 VectorNormalize(edge[0]);
206 VectorNormalize(edge[1]);
207 VectorNormalize(edge[2]);
208 CrossProduct(facenormal, edge[0], edgenormal[0]);
209 CrossProduct(facenormal, edge[1], edgenormal[1]);
210 CrossProduct(facenormal, edge[2], edgenormal[2]);
212 if (samelevel.integer & 1)
213 VectorNegate(edgenormal[0], edgenormal[0]);
214 if (samelevel.integer & 2)
215 VectorNegate(edgenormal[1], edgenormal[1]);
216 if (samelevel.integer & 4)
217 VectorNegate(edgenormal[2], edgenormal[2]);
218 for (i = 0;i < 3;i++)
219 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
220 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
221 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
222 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]);
225 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
226 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
227 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
229 // we got lucky, the center is within the face
230 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
234 if (info->bestdist > dist)
236 info->bestdist = dist;
237 VectorScale(facenormal, (info->radius - -dist), info->nudge);
242 if (info->bestdist > dist)
244 info->bestdist = dist;
245 VectorScale(facenormal, (info->radius - dist), info->nudge);
251 // check which edge or vertex the center is nearest
252 for (i = 0;i < 3;i++)
254 f = DotProduct(info->center, edge[i]);
255 if (f >= DotProduct(vert[0], edge[i])
256 && f <= DotProduct(vert[1], edge[i]))
259 VectorMA(info->center, -f, edge[i], point);
260 dist = sqrt(DotProduct(point, point));
261 if (info->bestdist > dist)
263 info->bestdist = dist;
264 VectorScale(point, (info->radius / dist), info->nudge);
266 // skip both vertex checks
267 // (both are further away than this edge)
272 // not on edge, check first vertex of edge
273 VectorSubtract(info->center, vert[i], point);
274 dist = sqrt(DotProduct(point, point));
275 if (info->bestdist > dist)
277 info->bestdist = dist;
278 VectorScale(point, (info->radius / dist), info->nudge);
291 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
295 if (((mleaf_t *)node)->nummarksurfaces)
296 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
300 float f = PlaneDiff(info->center, node->plane);
301 if (f >= -info->bestdist)
302 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
303 if (f <= info->bestdist)
304 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
308 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
311 findnonsolidlocationinfo_t info;
317 VectorCopy(in, info.center);
318 info.radius = radius;
323 VectorClear(info.nudge);
324 info.bestdist = radius;
325 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
326 VectorAdd(info.center, info.nudge, info.center);
328 while (info.bestdist < radius && ++i < 10);
329 VectorCopy(info.center, out);
332 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
334 switch(nativecontents)
339 return SUPERCONTENTS_SOLID;
341 return SUPERCONTENTS_WATER;
343 return SUPERCONTENTS_SLIME;
345 return SUPERCONTENTS_LAVA;
347 return SUPERCONTENTS_SKY;
352 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
354 if (supercontents & SUPERCONTENTS_SOLID)
355 return CONTENTS_SOLID;
356 if (supercontents & SUPERCONTENTS_SKY)
358 if (supercontents & SUPERCONTENTS_LAVA)
359 return CONTENTS_LAVA;
360 if (supercontents & SUPERCONTENTS_SLIME)
361 return CONTENTS_SLIME;
362 if (supercontents & SUPERCONTENTS_WATER)
363 return CONTENTS_WATER;
364 return CONTENTS_EMPTY;
369 // the hull we're tracing through
372 // the trace structure to fill in
375 // start, end, and end - start (in model space)
380 RecursiveHullCheckTraceInfo_t;
382 // 1/32 epsilon to keep floating point happy
383 #define DIST_EPSILON (0.03125)
385 #define HULLCHECKSTATE_EMPTY 0
386 #define HULLCHECKSTATE_SOLID 1
387 #define HULLCHECKSTATE_DONE 2
389 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
391 // status variables, these don't need to be saved on the stack when
392 // recursing... but are because this should be thread-safe
393 // (note: tracing against a bbox is not thread-safe, yet)
398 // variables that need to be stored on the stack when recursing
403 // LordHavoc: a goto! everyone flee in terror... :)
408 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
409 if (!t->trace->startfound)
411 t->trace->startfound = true;
412 t->trace->startsupercontents |= num;
414 if (num & SUPERCONTENTS_LIQUIDSMASK)
415 t->trace->inwater = true;
417 t->trace->inopen = true;
418 if (num & t->trace->hitsupercontentsmask)
420 // if the first leaf is solid, set startsolid
421 if (t->trace->allsolid)
422 t->trace->startsolid = true;
423 return HULLCHECKSTATE_SOLID;
427 t->trace->allsolid = false;
428 return HULLCHECKSTATE_EMPTY;
432 // find the point distances
433 node = t->hull->clipnodes + num;
435 plane = t->hull->planes + node->planenum;
438 t1 = p1[plane->type] - plane->dist;
439 t2 = p2[plane->type] - plane->dist;
443 t1 = DotProduct (plane->normal, p1) - plane->dist;
444 t2 = DotProduct (plane->normal, p2) - plane->dist;
451 num = node->children[1];
460 num = node->children[0];
466 // the line intersects, find intersection point
467 // LordHavoc: this uses the original trace for maximum accuracy
470 t1 = t->start[plane->type] - plane->dist;
471 t2 = t->end[plane->type] - plane->dist;
475 t1 = DotProduct (plane->normal, t->start) - plane->dist;
476 t2 = DotProduct (plane->normal, t->end) - plane->dist;
479 midf = t1 / (t1 - t2);
480 midf = bound(p1f, midf, p2f);
481 VectorMA(t->start, midf, t->dist, mid);
483 // recurse both sides, front side first
484 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
485 // if this side is not empty, return what it is (solid or done)
486 if (ret != HULLCHECKSTATE_EMPTY)
489 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
490 // if other side is not solid, return what it is (empty or done)
491 if (ret != HULLCHECKSTATE_SOLID)
494 // front is air and back is solid, this is the impact point...
497 t->trace->plane.dist = -plane->dist;
498 VectorNegate (plane->normal, t->trace->plane.normal);
502 t->trace->plane.dist = plane->dist;
503 VectorCopy (plane->normal, t->trace->plane.normal);
506 // bias away from surface a bit
507 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
508 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
510 midf = t1 / (t1 - t2);
511 t->trace->fraction = bound(0.0f, midf, 1.0);
513 return HULLCHECKSTATE_DONE;
516 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, int hitsupercontentsmask)
518 // this function currently only supports same size start and end
520 RecursiveHullCheckTraceInfo_t rhc;
522 memset(&rhc, 0, sizeof(rhc));
523 memset(trace, 0, sizeof(trace_t));
525 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
526 rhc.trace->fraction = 1;
527 rhc.trace->allsolid = true;
528 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
530 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
531 else if (model->brush.ishlbsp)
533 if (boxsize[0] <= 32)
535 if (boxsize[2] < 54) // pick the nearest of 36 or 72
536 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
538 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
541 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
545 if (boxsize[0] <= 32)
546 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
548 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
550 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
551 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
552 VectorSubtract(rhc.end, rhc.start, rhc.dist);
553 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
556 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)
558 int side, distz = endz - startz;
563 if (node->contents < 0)
564 return false; // didn't hit anything
566 switch (node->plane->type)
569 node = node->children[x < node->plane->dist];
572 node = node->children[y < node->plane->dist];
575 side = startz < node->plane->dist;
576 if ((endz < node->plane->dist) == side)
578 node = node->children[side];
581 // found an intersection
582 mid = node->plane->dist;
585 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
586 front += startz * node->plane->normal[2];
587 back += endz * node->plane->normal[2];
588 side = front < node->plane->dist;
589 if ((back < node->plane->dist) == side)
591 node = node->children[side];
594 // found an intersection
595 mid = startz + distz * (front - node->plane->dist) / (front - back);
599 // go down front side
600 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
601 return true; // hit something
604 // check for impact on this node
605 if (node->numsurfaces)
610 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
611 for (i = 0;i < node->numsurfaces;i++, surf++)
613 if (!(surf->flags & SURF_LIGHTMAP))
614 continue; // no lightmaps
616 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]);
617 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]);
619 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
622 ds -= surf->texturemins[0];
623 dt -= surf->texturemins[1];
625 if (ds > surf->extents[0] || dt > surf->extents[1])
631 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;
632 line3 = ((surf->extents[0]>>4)+1)*3;
633 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
635 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
637 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
639 scale = d_lightstylevalue[surf->styles[maps]];
640 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
641 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
642 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
643 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
648 LordHavoc: here's the readable version of the interpolation
649 code, not quite as easy for the compiler to optimize...
651 dsfrac is the X position in the lightmap pixel, * 16
652 dtfrac is the Y position in the lightmap pixel, * 16
653 r00 is top left corner, r01 is top right corner
654 r10 is bottom left corner, r11 is bottom right corner
655 g and b are the same layout.
656 r0 and r1 are the top and bottom intermediate results
658 first we interpolate the top two points, to get the top
661 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
662 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
663 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
665 then we interpolate the bottom two points, to get the
668 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
669 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
670 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
672 then we interpolate the top and bottom samples to get the
673 middle sample (the one which was requested)
675 r = (((r1-r0) * dtfrac) >> 4) + r0;
676 g = (((g1-g0) * dtfrac) >> 4) + g0;
677 b = (((b1-b0) * dtfrac) >> 4) + b0;
680 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
681 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
682 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
684 return true; // success
689 node = node->children[side ^ 1];
691 distz = endz - startz;
696 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
698 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);
701 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
708 Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
716 for (c = *in++;c > 0;c--)
720 Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
729 static void Mod_Q1BSP_LoadTextures(lump_t *l)
731 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
733 texture_t *tx, *tx2, *anims[10], *altanims[10];
735 qbyte *data, *mtdata;
738 loadmodel->brushq1.textures = NULL;
743 m = (dmiptexlump_t *)(mod_base + l->fileofs);
745 m->nummiptex = LittleLong (m->nummiptex);
747 // add two slots for notexture walls and notexture liquids
748 loadmodel->brushq1.numtextures = m->nummiptex + 2;
749 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
751 // fill out all slots with notexture
752 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
755 strcpy(tx->name, "NO TEXTURE FOUND");
758 tx->skin.base = r_notexture;
759 tx->shader = &Cshader_wall_lightmap;
760 tx->flags = SURF_SOLIDCLIP;
761 if (i == loadmodel->brushq1.numtextures - 1)
763 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
764 tx->shader = &Cshader_water;
766 tx->currentframe = tx;
769 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
771 // LordHavoc: mostly rewritten map texture loader
772 for (i = 0;i < m->nummiptex;i++)
774 dofs[i] = LittleLong(dofs[i]);
775 if (dofs[i] == -1 || r_nosurftextures.integer)
777 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
779 // make sure name is no more than 15 characters
780 for (j = 0;dmiptex->name[j] && j < 15;j++)
781 name[j] = dmiptex->name[j];
784 mtwidth = LittleLong(dmiptex->width);
785 mtheight = LittleLong(dmiptex->height);
787 j = LittleLong(dmiptex->offsets[0]);
791 if (j < 40 || j + mtwidth * mtheight > l->filelen)
793 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
796 mtdata = (qbyte *)dmiptex + j;
799 if ((mtwidth & 15) || (mtheight & 15))
800 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
802 // LordHavoc: force all names to lowercase
803 for (j = 0;name[j];j++)
804 if (name[j] >= 'A' && name[j] <= 'Z')
805 name[j] += 'a' - 'A';
807 tx = loadmodel->brushq1.textures + i;
808 strcpy(tx->name, name);
810 tx->height = mtheight;
814 sprintf(tx->name, "unnamed%i", i);
815 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
818 // LordHavoc: HL sky textures are entirely different than quake
819 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
821 if (loadmodel->isworldmodel)
823 data = loadimagepixels(tx->name, false, 0, 0);
826 if (image_width == 256 && image_height == 128)
834 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
836 R_InitSky(mtdata, 1);
839 else if (mtdata != NULL)
840 R_InitSky(mtdata, 1);
845 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
847 // did not find external texture, load it from the bsp or wad3
848 if (loadmodel->brush.ishlbsp)
850 // internal texture overrides wad
851 qbyte *pixels, *freepixels, *fogpixels;
852 pixels = freepixels = NULL;
854 pixels = W_ConvertWAD3Texture(dmiptex);
856 pixels = freepixels = W_GetTexture(tx->name);
859 tx->width = image_width;
860 tx->height = image_height;
861 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);
862 if (Image_CheckAlpha(pixels, image_width * image_height, true))
864 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
865 for (j = 0;j < image_width * image_height * 4;j += 4)
867 fogpixels[j + 0] = 255;
868 fogpixels[j + 1] = 255;
869 fogpixels[j + 2] = 255;
870 fogpixels[j + 3] = pixels[j + 3];
872 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
877 Mem_Free(freepixels);
879 else if (mtdata) // texture included
880 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
883 if (tx->skin.base == NULL)
888 tx->skin.base = r_notexture;
891 if (tx->name[0] == '*')
893 // turb does not block movement
894 tx->flags &= ~SURF_SOLIDCLIP;
895 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
896 // LordHavoc: some turbulent textures should be fullbright and solid
897 if (!strncmp(tx->name,"*lava",5)
898 || !strncmp(tx->name,"*teleport",9)
899 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
900 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
902 tx->flags |= SURF_WATERALPHA;
903 tx->shader = &Cshader_water;
905 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
907 tx->flags |= SURF_DRAWSKY;
908 tx->shader = &Cshader_sky;
912 tx->flags |= SURF_LIGHTMAP;
914 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
915 tx->shader = &Cshader_wall_lightmap;
918 // start out with no animation
919 tx->currentframe = tx;
922 // sequence the animations
923 for (i = 0;i < m->nummiptex;i++)
925 tx = loadmodel->brushq1.textures + i;
926 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
928 if (tx->anim_total[0] || tx->anim_total[1])
929 continue; // already sequenced
931 // find the number of frames in the animation
932 memset(anims, 0, sizeof(anims));
933 memset(altanims, 0, sizeof(altanims));
935 for (j = i;j < m->nummiptex;j++)
937 tx2 = loadmodel->brushq1.textures + j;
938 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
942 if (num >= '0' && num <= '9')
943 anims[num - '0'] = tx2;
944 else if (num >= 'a' && num <= 'j')
945 altanims[num - 'a'] = tx2;
947 Con_Printf("Bad animating texture %s\n", tx->name);
951 for (j = 0;j < 10;j++)
958 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
961 for (j = 0;j < max;j++)
965 Con_Printf("Missing frame %i of %s\n", j, tx->name);
969 for (j = 0;j < altmax;j++)
973 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
982 // if there is no alternate animation, duplicate the primary
983 // animation into the alternate
985 for (k = 0;k < 10;k++)
986 altanims[k] = anims[k];
989 // link together the primary animation
990 for (j = 0;j < max;j++)
993 tx2->animated = true;
994 tx2->anim_total[0] = max;
995 tx2->anim_total[1] = altmax;
996 for (k = 0;k < 10;k++)
998 tx2->anim_frames[0][k] = anims[k];
999 tx2->anim_frames[1][k] = altanims[k];
1003 // if there really is an alternate anim...
1004 if (anims[0] != altanims[0])
1006 // link together the alternate animation
1007 for (j = 0;j < altmax;j++)
1010 tx2->animated = true;
1011 // the primary/alternate are reversed here
1012 tx2->anim_total[0] = altmax;
1013 tx2->anim_total[1] = max;
1014 for (k = 0;k < 10;k++)
1016 tx2->anim_frames[0][k] = altanims[k];
1017 tx2->anim_frames[1][k] = anims[k];
1024 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1027 qbyte *in, *out, *data, d;
1028 char litfilename[1024];
1029 loadmodel->brushq1.lightdata = NULL;
1030 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1032 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1033 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1035 else // LordHavoc: bsp version 29 (normal white lighting)
1037 // LordHavoc: hope is not lost yet, check for a .lit file to load
1038 strcpy(litfilename, loadmodel->name);
1039 FS_StripExtension(litfilename, litfilename);
1040 strcat(litfilename, ".lit");
1041 data = (qbyte*) FS_LoadFile(litfilename, false);
1044 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1046 i = LittleLong(((int *)data)[1]);
1049 Con_DPrintf("loaded %s\n", litfilename);
1050 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1051 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1057 Con_Printf("Unknown .lit file version (%d)\n", i);
1063 if (fs_filesize == 8)
1064 Con_Printf("Empty .lit file, ignoring\n");
1066 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1070 // LordHavoc: oh well, expand the white lighting data
1073 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1074 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1075 out = loadmodel->brushq1.lightdata;
1076 memcpy(in, mod_base + l->fileofs, l->filelen);
1077 for (i = 0;i < l->filelen;i++)
1087 static void Mod_Q1BSP_LoadLightList(void)
1089 int a, n, numlights;
1090 char lightsfilename[1024], *s, *t, *lightsstring;
1093 strcpy(lightsfilename, loadmodel->name);
1094 FS_StripExtension(lightsfilename, lightsfilename);
1095 strcat(lightsfilename, ".lights");
1096 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1102 while (*s && *s != '\n')
1106 Mem_Free(lightsstring);
1107 Host_Error("lights file must end with a newline\n");
1112 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1115 while (*s && n < numlights)
1118 while (*s && *s != '\n')
1122 Mem_Free(lightsstring);
1123 Host_Error("misparsed lights file!\n");
1125 e = loadmodel->brushq1.lights + n;
1127 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);
1131 Mem_Free(lightsstring);
1132 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);
1139 Mem_Free(lightsstring);
1140 Host_Error("misparsed lights file!\n");
1142 loadmodel->brushq1.numlights = numlights;
1143 Mem_Free(lightsstring);
1147 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1149 loadmodel->brushq1.num_compressedpvs = 0;
1150 loadmodel->brushq1.data_compressedpvs = NULL;
1153 loadmodel->brushq1.num_compressedpvs = l->filelen;
1154 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1155 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1158 // used only for HalfLife maps
1159 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1161 char key[128], value[4096];
1166 if (!COM_ParseToken(&data, false))
1168 if (com_token[0] != '{')
1172 if (!COM_ParseToken(&data, false))
1174 if (com_token[0] == '}')
1175 break; // end of worldspawn
1176 if (com_token[0] == '_')
1177 strcpy(key, com_token + 1);
1179 strcpy(key, com_token);
1180 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1181 key[strlen(key)-1] = 0;
1182 if (!COM_ParseToken(&data, false))
1184 strcpy(value, com_token);
1185 if (!strcmp("wad", key)) // for HalfLife maps
1187 if (loadmodel->brush.ishlbsp)
1190 for (i = 0;i < 4096;i++)
1191 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1197 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1198 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1200 else if (value[i] == ';' || value[i] == 0)
1204 strcpy(wadname, "textures/");
1205 strcat(wadname, &value[j]);
1206 W_LoadTextureWadFile(wadname, false);
1218 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1220 loadmodel->brush.entities = NULL;
1223 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1224 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1225 if (loadmodel->brush.ishlbsp)
1226 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1230 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1236 in = (void *)(mod_base + l->fileofs);
1237 if (l->filelen % sizeof(*in))
1238 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1239 count = l->filelen / sizeof(*in);
1240 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1242 loadmodel->brushq1.vertexes = out;
1243 loadmodel->brushq1.numvertexes = count;
1245 for ( i=0 ; i<count ; i++, in++, out++)
1247 out->position[0] = LittleFloat(in->point[0]);
1248 out->position[1] = LittleFloat(in->point[1]);
1249 out->position[2] = LittleFloat(in->point[2]);
1253 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1259 in = (void *)(mod_base + l->fileofs);
1260 if (l->filelen % sizeof(*in))
1261 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1262 count = l->filelen / sizeof(*in);
1263 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1265 loadmodel->brushq1.submodels = out;
1266 loadmodel->brush.numsubmodels = count;
1268 for ( i=0 ; i<count ; i++, in++, out++)
1270 for (j=0 ; j<3 ; j++)
1272 // spread the mins / maxs by a pixel
1273 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1274 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1275 out->origin[j] = LittleFloat(in->origin[j]);
1277 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1278 out->headnode[j] = LittleLong(in->headnode[j]);
1279 out->visleafs = LittleLong(in->visleafs);
1280 out->firstface = LittleLong(in->firstface);
1281 out->numfaces = LittleLong(in->numfaces);
1285 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1291 in = (void *)(mod_base + l->fileofs);
1292 if (l->filelen % sizeof(*in))
1293 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1294 count = l->filelen / sizeof(*in);
1295 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1297 loadmodel->brushq1.edges = out;
1298 loadmodel->brushq1.numedges = count;
1300 for ( i=0 ; i<count ; i++, in++, out++)
1302 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1303 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1307 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1311 int i, j, k, count, miptex;
1313 in = (void *)(mod_base + l->fileofs);
1314 if (l->filelen % sizeof(*in))
1315 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1316 count = l->filelen / sizeof(*in);
1317 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1319 loadmodel->brushq1.texinfo = out;
1320 loadmodel->brushq1.numtexinfo = count;
1322 for (i = 0;i < count;i++, in++, out++)
1324 for (k = 0;k < 2;k++)
1325 for (j = 0;j < 4;j++)
1326 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1328 miptex = LittleLong(in->miptex);
1329 out->flags = LittleLong(in->flags);
1331 out->texture = NULL;
1332 if (loadmodel->brushq1.textures)
1334 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1335 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1337 out->texture = loadmodel->brushq1.textures + miptex;
1339 if (out->flags & TEX_SPECIAL)
1341 // if texture chosen is NULL or the shader needs a lightmap,
1342 // force to notexture water shader
1343 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1344 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1348 // if texture chosen is NULL, force to notexture
1349 if (out->texture == NULL)
1350 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1356 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1361 mins[0] = mins[1] = mins[2] = 9999;
1362 maxs[0] = maxs[1] = maxs[2] = -9999;
1364 for (i = 0;i < numverts;i++)
1366 for (j = 0;j < 3;j++, v++)
1376 #define MAX_SUBDIVPOLYTRIANGLES 4096
1377 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1379 static int subdivpolyverts, subdivpolytriangles;
1380 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1381 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1383 static int subdivpolylookupvert(vec3_t v)
1386 for (i = 0;i < subdivpolyverts;i++)
1387 if (subdivpolyvert[i][0] == v[0]
1388 && subdivpolyvert[i][1] == v[1]
1389 && subdivpolyvert[i][2] == v[2])
1391 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1392 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1393 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1394 return subdivpolyverts++;
1397 static void SubdividePolygon(int numverts, float *verts)
1399 int i, i1, i2, i3, f, b, c, p;
1400 vec3_t mins, maxs, front[256], back[256];
1401 float m, *pv, *cv, dist[256], frac;
1404 Host_Error("SubdividePolygon: ran out of verts in buffer");
1406 BoundPoly(numverts, verts, mins, maxs);
1408 for (i = 0;i < 3;i++)
1410 m = (mins[i] + maxs[i]) * 0.5;
1411 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1412 if (maxs[i] - m < 8)
1414 if (m - mins[i] < 8)
1418 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1419 dist[c] = cv[i] - m;
1422 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1426 VectorCopy(pv, front[f]);
1431 VectorCopy(pv, back[b]);
1434 if (dist[p] == 0 || dist[c] == 0)
1436 if ((dist[p] > 0) != (dist[c] > 0) )
1439 frac = dist[p] / (dist[p] - dist[c]);
1440 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1441 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1442 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1448 SubdividePolygon(f, front[0]);
1449 SubdividePolygon(b, back[0]);
1453 i1 = subdivpolylookupvert(verts);
1454 i2 = subdivpolylookupvert(verts + 3);
1455 for (i = 2;i < numverts;i++)
1457 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1459 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1463 i3 = subdivpolylookupvert(verts + i * 3);
1464 subdivpolyindex[subdivpolytriangles][0] = i1;
1465 subdivpolyindex[subdivpolytriangles][1] = i2;
1466 subdivpolyindex[subdivpolytriangles][2] = i3;
1468 subdivpolytriangles++;
1472 //Breaks a polygon up along axial 64 unit
1473 //boundaries so that turbulent and sky warps
1474 //can be done reasonably.
1475 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1481 subdivpolytriangles = 0;
1482 subdivpolyverts = 0;
1483 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1484 if (subdivpolytriangles < 1)
1485 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1487 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1488 mesh->numverts = subdivpolyverts;
1489 mesh->numtriangles = subdivpolytriangles;
1490 mesh->vertex = (surfvertex_t *)(mesh + 1);
1491 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1492 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1494 for (i = 0;i < mesh->numtriangles;i++)
1495 for (j = 0;j < 3;j++)
1496 mesh->index[i*3+j] = subdivpolyindex[i][j];
1498 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1500 VectorCopy(subdivpolyvert[i], v->v);
1501 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1502 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1507 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1510 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1511 mesh->numverts = numverts;
1512 mesh->numtriangles = numtriangles;
1513 mesh->vertex3f = (float *)(mesh + 1);
1514 mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1515 mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1516 mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1517 mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1518 mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1519 mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1520 mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1521 mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1522 mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1526 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1529 float *vec, *vert, mins[3], maxs[3], val, *v;
1532 // convert edges back to a normal polygon
1533 surf->poly_numverts = numedges;
1534 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1535 for (i = 0;i < numedges;i++)
1537 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1539 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1541 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1542 VectorCopy(vec, vert);
1546 // calculate polygon bounding box and center
1547 vert = surf->poly_verts;
1548 VectorCopy(vert, mins);
1549 VectorCopy(vert, maxs);
1551 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1553 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1554 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1555 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1557 VectorCopy(mins, surf->poly_mins);
1558 VectorCopy(maxs, surf->poly_maxs);
1559 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1560 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1561 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1563 // generate surface extents information
1564 tex = surf->texinfo;
1565 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1566 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1567 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1569 for (j = 0;j < 2;j++)
1571 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1578 for (i = 0;i < 2;i++)
1580 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1581 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1585 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1589 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1593 in = (void *)(mod_base + l->fileofs);
1594 if (l->filelen % sizeof(*in))
1595 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1596 count = l->filelen / sizeof(*in);
1597 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1599 loadmodel->brushq1.numsurfaces = count;
1600 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1601 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1602 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1604 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++)
1606 surf->number = surfnum;
1607 // FIXME: validate edges, texinfo, etc?
1608 firstedge = LittleLong(in->firstedge);
1609 numedges = LittleShort(in->numedges);
1610 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)
1611 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1612 i = LittleShort(in->texinfo);
1613 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1614 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1615 surf->texinfo = loadmodel->brushq1.texinfo + i;
1616 surf->flags = surf->texinfo->texture->flags;
1618 planenum = LittleShort(in->planenum);
1619 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1620 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1622 if (LittleShort(in->side))
1623 surf->flags |= SURF_PLANEBACK;
1625 surf->plane = loadmodel->brushq1.planes + planenum;
1627 // clear lightmap (filled in later)
1628 surf->lightmaptexture = NULL;
1630 // force lightmap upload on first time seeing the surface
1631 surf->cached_dlight = true;
1633 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1635 ssize = (surf->extents[0] >> 4) + 1;
1636 tsize = (surf->extents[1] >> 4) + 1;
1639 for (i = 0;i < MAXLIGHTMAPS;i++)
1640 surf->styles[i] = in->styles[i];
1641 i = LittleLong(in->lightofs);
1643 surf->samples = NULL;
1644 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1645 surf->samples = loadmodel->brushq1.lightdata + i;
1646 else // LordHavoc: white lighting (bsp version 29)
1647 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1649 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1651 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1652 Host_Error("Bad surface extents");
1653 // stainmap for permanent marks on walls
1654 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1656 memset(surf->stainsamples, 255, ssize * tsize * 3);
1660 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1661 loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1663 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++)
1665 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1666 mesh->numverts = surf->poly_numverts;
1667 mesh->numtriangles = surf->poly_numverts - 2;
1668 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1669 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1670 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1671 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1672 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1673 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1674 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1675 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1676 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1677 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1679 surf->lightmaptexturestride = 0;
1680 surf->lightmaptexture = NULL;
1682 for (i = 0;i < mesh->numverts;i++)
1684 mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1685 mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1686 mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1687 s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1688 t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1689 mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1690 mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1691 mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1692 mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1693 mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1694 mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1695 mesh->lightmapoffsets[i] = 0;
1698 for (i = 0;i < mesh->numtriangles;i++)
1700 mesh->element3i[i * 3 + 0] = 0;
1701 mesh->element3i[i * 3 + 1] = i + 1;
1702 mesh->element3i[i * 3 + 2] = i + 2;
1705 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1706 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1708 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1710 int i, iu, iv, smax, tmax;
1711 float u, v, ubase, vbase, uscale, vscale;
1713 smax = surf->extents[0] >> 4;
1714 tmax = surf->extents[1] >> 4;
1716 surf->flags |= SURF_LIGHTMAP;
1717 if (r_miplightmaps.integer)
1719 surf->lightmaptexturestride = smax+1;
1720 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);
1724 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1725 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);
1727 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1728 uscale = (uscale - ubase) / (smax + 1);
1729 vscale = (vscale - vbase) / (tmax + 1);
1731 for (i = 0;i < mesh->numverts;i++)
1733 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1734 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1735 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1736 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1737 // LordHavoc: calc lightmap data offset for vertex lighting to use
1740 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1746 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1748 node->parent = parent;
1749 if (node->contents < 0)
1751 Mod_Q1BSP_SetParent(node->children[0], node);
1752 Mod_Q1BSP_SetParent(node->children[1], node);
1755 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1761 in = (void *)(mod_base + l->fileofs);
1762 if (l->filelen % sizeof(*in))
1763 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1764 count = l->filelen / sizeof(*in);
1765 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1767 loadmodel->brushq1.nodes = out;
1768 loadmodel->brushq1.numnodes = count;
1770 for ( i=0 ; i<count ; i++, in++, out++)
1772 for (j=0 ; j<3 ; j++)
1774 out->mins[j] = LittleShort(in->mins[j]);
1775 out->maxs[j] = LittleShort(in->maxs[j]);
1778 p = LittleLong(in->planenum);
1779 out->plane = loadmodel->brushq1.planes + p;
1781 out->firstsurface = LittleShort(in->firstface);
1782 out->numsurfaces = LittleShort(in->numfaces);
1784 for (j=0 ; j<2 ; j++)
1786 p = LittleShort(in->children[j]);
1788 out->children[j] = loadmodel->brushq1.nodes + p;
1790 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1794 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1797 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1801 int i, j, count, p, pvschainbytes;
1804 in = (void *)(mod_base + l->fileofs);
1805 if (l->filelen % sizeof(*in))
1806 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1807 count = l->filelen / sizeof(*in);
1808 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1810 loadmodel->brushq1.leafs = out;
1811 loadmodel->brushq1.numleafs = count;
1812 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1813 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1815 for ( i=0 ; i<count ; i++, in++, out++)
1817 for (j=0 ; j<3 ; j++)
1819 out->mins[j] = LittleShort(in->mins[j]);
1820 out->maxs[j] = LittleShort(in->maxs[j]);
1823 // FIXME: this function could really benefit from some error checking
1825 out->contents = LittleLong(in->contents);
1827 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1828 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1831 pvs += pvschainbytes;
1833 p = LittleLong(in->visofs);
1835 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1837 memset(out->pvsdata, 0xFF, pvschainbytes);
1839 for (j = 0;j < 4;j++)
1840 out->ambient_sound_level[j] = in->ambient_level[j];
1842 // FIXME: Insert caustics here
1846 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1848 dclipnode_t *in, *out;
1852 in = (void *)(mod_base + l->fileofs);
1853 if (l->filelen % sizeof(*in))
1854 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1855 count = l->filelen / sizeof(*in);
1856 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1858 loadmodel->brushq1.clipnodes = out;
1859 loadmodel->brushq1.numclipnodes = count;
1861 if (loadmodel->brush.ishlbsp)
1863 hull = &loadmodel->brushq1.hulls[1];
1864 hull->clipnodes = out;
1865 hull->firstclipnode = 0;
1866 hull->lastclipnode = count-1;
1867 hull->planes = loadmodel->brushq1.planes;
1868 hull->clip_mins[0] = -16;
1869 hull->clip_mins[1] = -16;
1870 hull->clip_mins[2] = -36;
1871 hull->clip_maxs[0] = 16;
1872 hull->clip_maxs[1] = 16;
1873 hull->clip_maxs[2] = 36;
1874 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1876 hull = &loadmodel->brushq1.hulls[2];
1877 hull->clipnodes = out;
1878 hull->firstclipnode = 0;
1879 hull->lastclipnode = count-1;
1880 hull->planes = loadmodel->brushq1.planes;
1881 hull->clip_mins[0] = -32;
1882 hull->clip_mins[1] = -32;
1883 hull->clip_mins[2] = -32;
1884 hull->clip_maxs[0] = 32;
1885 hull->clip_maxs[1] = 32;
1886 hull->clip_maxs[2] = 32;
1887 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1889 hull = &loadmodel->brushq1.hulls[3];
1890 hull->clipnodes = out;
1891 hull->firstclipnode = 0;
1892 hull->lastclipnode = count-1;
1893 hull->planes = loadmodel->brushq1.planes;
1894 hull->clip_mins[0] = -16;
1895 hull->clip_mins[1] = -16;
1896 hull->clip_mins[2] = -18;
1897 hull->clip_maxs[0] = 16;
1898 hull->clip_maxs[1] = 16;
1899 hull->clip_maxs[2] = 18;
1900 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1904 hull = &loadmodel->brushq1.hulls[1];
1905 hull->clipnodes = out;
1906 hull->firstclipnode = 0;
1907 hull->lastclipnode = count-1;
1908 hull->planes = loadmodel->brushq1.planes;
1909 hull->clip_mins[0] = -16;
1910 hull->clip_mins[1] = -16;
1911 hull->clip_mins[2] = -24;
1912 hull->clip_maxs[0] = 16;
1913 hull->clip_maxs[1] = 16;
1914 hull->clip_maxs[2] = 32;
1915 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1917 hull = &loadmodel->brushq1.hulls[2];
1918 hull->clipnodes = out;
1919 hull->firstclipnode = 0;
1920 hull->lastclipnode = count-1;
1921 hull->planes = loadmodel->brushq1.planes;
1922 hull->clip_mins[0] = -32;
1923 hull->clip_mins[1] = -32;
1924 hull->clip_mins[2] = -24;
1925 hull->clip_maxs[0] = 32;
1926 hull->clip_maxs[1] = 32;
1927 hull->clip_maxs[2] = 64;
1928 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1931 for (i=0 ; i<count ; i++, out++, in++)
1933 out->planenum = LittleLong(in->planenum);
1934 out->children[0] = LittleShort(in->children[0]);
1935 out->children[1] = LittleShort(in->children[1]);
1936 if (out->children[0] >= count || out->children[1] >= count)
1937 Host_Error("Corrupt clipping hull(out of range child)\n");
1941 //Duplicate the drawing hull structure as a clipping hull
1942 static void Mod_Q1BSP_MakeHull0(void)
1949 hull = &loadmodel->brushq1.hulls[0];
1951 in = loadmodel->brushq1.nodes;
1952 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1954 hull->clipnodes = out;
1955 hull->firstclipnode = 0;
1956 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1957 hull->planes = loadmodel->brushq1.planes;
1959 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1961 out->planenum = in->plane - loadmodel->brushq1.planes;
1962 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1963 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1967 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1972 in = (void *)(mod_base + l->fileofs);
1973 if (l->filelen % sizeof(*in))
1974 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1975 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1976 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1978 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1980 j = (unsigned) LittleShort(in[i]);
1981 if (j >= loadmodel->brushq1.numsurfaces)
1982 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
1983 loadmodel->brushq1.marksurfaces[i] = j;
1987 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
1992 in = (void *)(mod_base + l->fileofs);
1993 if (l->filelen % sizeof(*in))
1994 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
1995 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
1996 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
1998 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
1999 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2003 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2009 in = (void *)(mod_base + l->fileofs);
2010 if (l->filelen % sizeof(*in))
2011 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2013 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2014 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2016 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2018 out->normal[0] = LittleFloat(in->normal[0]);
2019 out->normal[1] = LittleFloat(in->normal[1]);
2020 out->normal[2] = LittleFloat(in->normal[2]);
2021 out->dist = LittleFloat(in->dist);
2027 typedef struct portal_s
2030 mnode_t *nodes[2]; // [0] = front side of plane
2031 struct portal_s *next[2];
2033 struct portal_s *chain; // all portals are linked into a list
2037 static portal_t *portalchain;
2044 static portal_t *AllocPortal(void)
2047 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2048 p->chain = portalchain;
2053 static void FreePortal(portal_t *p)
2058 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2060 // calculate children first
2061 if (node->children[0]->contents >= 0)
2062 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2063 if (node->children[1]->contents >= 0)
2064 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2066 // make combined bounding box from children
2067 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2068 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2069 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2070 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2071 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2072 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2075 static void Mod_Q1BSP_FinalizePortals(void)
2077 int i, j, numportals, numpoints;
2078 portal_t *p, *pnext;
2081 mleaf_t *leaf, *endleaf;
2084 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2085 leaf = loadmodel->brushq1.leafs;
2086 endleaf = leaf + loadmodel->brushq1.numleafs;
2087 for (;leaf < endleaf;leaf++)
2089 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2090 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2097 for (i = 0;i < 2;i++)
2099 leaf = (mleaf_t *)p->nodes[i];
2101 for (j = 0;j < w->numpoints;j++)
2103 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2104 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2105 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2106 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2107 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2108 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2115 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2117 // tally up portal and point counts
2123 // note: this check must match the one below or it will usually corrupt memory
2124 // 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
2125 if (p->winding && p->nodes[0] != p->nodes[1]
2126 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2127 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2130 numpoints += p->winding->numpoints * 2;
2134 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2135 loadmodel->brushq1.numportals = numportals;
2136 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2137 loadmodel->brushq1.numportalpoints = numpoints;
2138 // clear all leaf portal chains
2139 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2140 loadmodel->brushq1.leafs[i].portals = NULL;
2141 // process all portals in the global portal chain, while freeing them
2142 portal = loadmodel->brushq1.portals;
2143 point = loadmodel->brushq1.portalpoints;
2152 // note: this check must match the one above or it will usually corrupt memory
2153 // 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
2154 if (p->nodes[0] != p->nodes[1]
2155 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2156 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2158 // first make the back to front portal(forward portal)
2159 portal->points = point;
2160 portal->numpoints = p->winding->numpoints;
2161 portal->plane.dist = p->plane.dist;
2162 VectorCopy(p->plane.normal, portal->plane.normal);
2163 portal->here = (mleaf_t *)p->nodes[1];
2164 portal->past = (mleaf_t *)p->nodes[0];
2166 for (j = 0;j < portal->numpoints;j++)
2168 VectorCopy(p->winding->points[j], point->position);
2171 PlaneClassify(&portal->plane);
2173 // link into leaf's portal chain
2174 portal->next = portal->here->portals;
2175 portal->here->portals = portal;
2177 // advance to next portal
2180 // then make the front to back portal(backward portal)
2181 portal->points = point;
2182 portal->numpoints = p->winding->numpoints;
2183 portal->plane.dist = -p->plane.dist;
2184 VectorNegate(p->plane.normal, portal->plane.normal);
2185 portal->here = (mleaf_t *)p->nodes[0];
2186 portal->past = (mleaf_t *)p->nodes[1];
2188 for (j = portal->numpoints - 1;j >= 0;j--)
2190 VectorCopy(p->winding->points[j], point->position);
2193 PlaneClassify(&portal->plane);
2195 // link into leaf's portal chain
2196 portal->next = portal->here->portals;
2197 portal->here->portals = portal;
2199 // advance to next portal
2202 Winding_Free(p->winding);
2214 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2217 Host_Error("AddPortalToNodes: NULL front node");
2219 Host_Error("AddPortalToNodes: NULL back node");
2220 if (p->nodes[0] || p->nodes[1])
2221 Host_Error("AddPortalToNodes: already included");
2222 // 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
2224 p->nodes[0] = front;
2225 p->next[0] = (portal_t *)front->portals;
2226 front->portals = (mportal_t *)p;
2229 p->next[1] = (portal_t *)back->portals;
2230 back->portals = (mportal_t *)p;
2235 RemovePortalFromNode
2238 static void RemovePortalFromNodes(portal_t *portal)
2242 void **portalpointer;
2244 for (i = 0;i < 2;i++)
2246 node = portal->nodes[i];
2248 portalpointer = (void **) &node->portals;
2253 Host_Error("RemovePortalFromNodes: portal not in leaf");
2257 if (portal->nodes[0] == node)
2259 *portalpointer = portal->next[0];
2260 portal->nodes[0] = NULL;
2262 else if (portal->nodes[1] == node)
2264 *portalpointer = portal->next[1];
2265 portal->nodes[1] = NULL;
2268 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2272 if (t->nodes[0] == node)
2273 portalpointer = (void **) &t->next[0];
2274 else if (t->nodes[1] == node)
2275 portalpointer = (void **) &t->next[1];
2277 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2282 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2285 mnode_t *front, *back, *other_node;
2286 mplane_t clipplane, *plane;
2287 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2288 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2290 // if a leaf, we're done
2294 plane = node->plane;
2296 front = node->children[0];
2297 back = node->children[1];
2299 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2301 // create the new portal by generating a polygon for the node plane,
2302 // and clipping it by all of the other portals(which came from nodes above this one)
2303 nodeportal = AllocPortal();
2304 nodeportal->plane = *node->plane;
2306 nodeportalwinding = Winding_NewFromPlane(node->plane->normal[0], node->plane->normal[1], node->plane->normal[2], node->plane->dist);
2307 side = 0; // shut up compiler warning
2308 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2310 clipplane = portal->plane;
2311 if (portal->nodes[0] == portal->nodes[1])
2312 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2313 if (portal->nodes[0] == node)
2315 else if (portal->nodes[1] == node)
2317 clipplane.dist = -clipplane.dist;
2318 VectorNegate(clipplane.normal, clipplane.normal);
2322 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2324 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2325 if (!nodeportalwinding)
2327 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2332 if (nodeportalwinding)
2334 // if the plane was not clipped on all sides, there was an error
2335 nodeportal->winding = nodeportalwinding;
2336 AddPortalToNodes(nodeportal, front, back);
2339 // split the portals of this node along this node's plane and assign them to the children of this node
2340 // (migrating the portals downward through the tree)
2341 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2343 if (portal->nodes[0] == portal->nodes[1])
2344 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2345 if (portal->nodes[0] == node)
2347 else if (portal->nodes[1] == node)
2350 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2351 nextportal = portal->next[side];
2353 other_node = portal->nodes[!side];
2354 RemovePortalFromNodes(portal);
2356 // cut the portal into two portals, one on each side of the node plane
2357 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2362 AddPortalToNodes(portal, back, other_node);
2364 AddPortalToNodes(portal, other_node, back);
2370 AddPortalToNodes(portal, front, other_node);
2372 AddPortalToNodes(portal, other_node, front);
2376 // the winding is split
2377 splitportal = AllocPortal();
2378 temp = splitportal->chain;
2379 *splitportal = *portal;
2380 splitportal->chain = temp;
2381 splitportal->winding = backwinding;
2382 Winding_Free(portal->winding);
2383 portal->winding = frontwinding;
2387 AddPortalToNodes(portal, front, other_node);
2388 AddPortalToNodes(splitportal, back, other_node);
2392 AddPortalToNodes(portal, other_node, front);
2393 AddPortalToNodes(splitportal, other_node, back);
2397 Mod_Q1BSP_RecursiveNodePortals(front);
2398 Mod_Q1BSP_RecursiveNodePortals(back);
2401 static void Mod_Q1BSP_MakePortals(void)
2404 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2405 Mod_Q1BSP_FinalizePortals();
2408 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2411 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2412 msurface_t *surf, *s;
2413 float *v0, *v1, *v2, *v3;
2414 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2415 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2416 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2418 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)
2420 if (surf->neighborsurfaces[vertnum])
2422 surf->neighborsurfaces[vertnum] = NULL;
2423 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2425 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2426 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2427 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2430 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2431 if (s->neighborsurfaces[vnum] == surf)
2433 if (vnum < s->poly_numverts)
2435 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)
2437 if (s->neighborsurfaces[vnum] == NULL
2438 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2439 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2441 surf->neighborsurfaces[vertnum] = s;
2442 s->neighborsurfaces[vnum] = surf;
2446 if (vnum < s->poly_numverts)
2454 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2456 int i, j, stylecounts[256], totalcount, remapstyles[256];
2458 memset(stylecounts, 0, sizeof(stylecounts));
2459 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2461 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2462 for (j = 0;j < MAXLIGHTMAPS;j++)
2463 stylecounts[surf->styles[j]]++;
2466 model->brushq1.light_styles = 0;
2467 for (i = 0;i < 255;i++)
2471 remapstyles[i] = model->brushq1.light_styles++;
2472 totalcount += stylecounts[i] + 1;
2477 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2478 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2479 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2480 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2481 model->brushq1.light_styles = 0;
2482 for (i = 0;i < 255;i++)
2484 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2486 for (i = 0;i < model->brushq1.light_styles;i++)
2488 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2489 j += stylecounts[model->brushq1.light_style[i]] + 1;
2491 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2493 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2494 for (j = 0;j < MAXLIGHTMAPS;j++)
2495 if (surf->styles[j] != 255)
2496 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2499 for (i = 0;i < model->brushq1.light_styles;i++)
2501 *model->brushq1.light_styleupdatechains[i] = NULL;
2502 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2503 j += stylecounts[model->brushq1.light_style[i]] + 1;
2507 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2510 for (i = 0;i < model->brushq1.numtextures;i++)
2511 model->brushq1.pvstexturechainslength[i] = 0;
2512 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2514 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2516 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2517 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2520 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2522 if (model->brushq1.pvstexturechainslength[i])
2524 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2525 j += model->brushq1.pvstexturechainslength[i] + 1;
2528 model->brushq1.pvstexturechains[i] = NULL;
2530 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2531 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2532 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2533 for (i = 0;i < model->brushq1.numtextures;i++)
2535 if (model->brushq1.pvstexturechainslength[i])
2537 *model->brushq1.pvstexturechains[i] = NULL;
2538 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2543 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2548 while (node->contents >= 0)
2550 d = PlaneDiff(org, node->plane);
2552 node = node->children[0];
2553 else if (d < -radius)
2554 node = node->children[1];
2557 // go down both sides
2558 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2559 node = node->children[1];
2563 // if this is a leaf, accumulate the pvs bits
2564 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2565 for (i = 0;i < pvsbytes;i++)
2566 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2569 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2570 //of the given point.
2571 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2573 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2574 bytes = min(bytes, pvsbufferlength);
2575 memset(pvsbuffer, 0, bytes);
2576 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2580 //Returns PVS data for a given point
2581 //(note: always returns valid data, never NULL)
2582 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2585 Mod_CheckLoaded(model);
2586 // LordHavoc: modified to start at first clip node,
2587 // in other words: first node of the (sub)model
2588 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2589 while (node->contents == 0)
2590 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2591 return ((mleaf_t *)node)->pvsdata;
2594 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2599 VectorSubtract(inmaxs, inmins, size);
2600 if (cmodel->brush.ishlbsp)
2603 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2604 else if (size[0] <= 32)
2606 if (size[2] < 54) // pick the nearest of 36 or 72
2607 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2609 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2612 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2617 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2618 else if (size[0] <= 32)
2619 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2621 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2623 VectorCopy(inmins, outmins);
2624 VectorAdd(inmins, hull->clip_size, outmaxs);
2627 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2628 extern void R_Model_Brush_Draw(entity_render_t *ent);
2629 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2630 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);
2631 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2636 mempool_t *mainmempool;
2638 model_t *originalloadmodel;
2639 float dist, modelyawradius, modelradius, *vec;
2643 mod->type = mod_brush;
2645 header = (dheader_t *)buffer;
2647 i = LittleLong(header->version);
2648 if (i != BSPVERSION && i != 30)
2649 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2650 mod->brush.ishlbsp = i == 30;
2652 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2653 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2654 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2655 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2656 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2657 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2658 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2659 mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
2660 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2661 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2662 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2663 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2665 if (loadmodel->isworldmodel)
2667 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2668 // until we get a texture for it...
2672 // swap all the lumps
2673 mod_base = (qbyte *)header;
2675 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2676 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2680 // store which lightmap format to use
2681 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2683 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2684 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2685 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2686 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2687 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2688 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2689 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2690 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2691 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2692 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2693 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2694 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2695 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2696 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2697 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2699 if (mod->brushq1.data_compressedpvs)
2700 Mem_Free(mod->brushq1.data_compressedpvs);
2701 mod->brushq1.data_compressedpvs = NULL;
2702 mod->brushq1.num_compressedpvs = 0;
2704 Mod_Q1BSP_MakeHull0();
2705 Mod_Q1BSP_MakePortals();
2707 if (developer.integer)
2708 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);
2710 mod->numframes = 2; // regular and alternate animation
2712 mainmempool = mod->mempool;
2713 loadname = mod->name;
2715 Mod_Q1BSP_LoadLightList();
2716 originalloadmodel = loadmodel;
2719 // set up the submodels(FIXME: this is confusing)
2721 for (i = 0;i < mod->brush.numsubmodels;i++)
2723 bm = &mod->brushq1.submodels[i];
2725 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2726 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2728 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2729 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2732 mod->brushq1.firstmodelsurface = bm->firstface;
2733 mod->brushq1.nummodelsurfaces = bm->numfaces;
2735 // this gets altered below if sky is used
2736 mod->DrawSky = NULL;
2737 mod->Draw = R_Model_Brush_Draw;
2738 mod->DrawFakeShadow = NULL;
2739 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2740 mod->DrawLight = R_Model_Brush_DrawLight;
2741 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2742 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2743 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2744 Mod_Q1BSP_BuildPVSTextureChains(mod);
2745 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2746 if (mod->brushq1.nummodelsurfaces)
2748 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2749 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2750 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2753 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2755 // we only need to have a drawsky function if it is used(usually only on world model)
2756 if (surf->texinfo->texture->shader == &Cshader_sky)
2757 mod->DrawSky = R_Model_Brush_DrawSky;
2758 // LordHavoc: submodels always clip, even if water
2759 if (mod->brush.numsubmodels - 1)
2760 surf->flags |= SURF_SOLIDCLIP;
2761 // calculate bounding shapes
2762 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2764 for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2766 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2767 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2768 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2769 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2770 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2771 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2772 dist = vec[0]*vec[0]+vec[1]*vec[1];
2773 if (modelyawradius < dist)
2774 modelyawradius = dist;
2775 dist += vec[2]*vec[2];
2776 if (modelradius < dist)
2781 modelyawradius = sqrt(modelyawradius);
2782 modelradius = sqrt(modelradius);
2783 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2784 mod->yawmins[2] = mod->normalmins[2];
2785 mod->yawmaxs[2] = mod->normalmaxs[2];
2786 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2787 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2788 mod->radius = modelradius;
2789 mod->radius2 = modelradius * modelradius;
2793 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2794 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2796 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2798 mod->brushq1.visleafs = bm->visleafs;
2800 // LordHavoc: only register submodels if it is the world
2801 // (prevents bsp models from replacing world submodels)
2802 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2805 // duplicate the basic information
2806 sprintf(name, "*%i", i+1);
2807 loadmodel = Mod_FindName(name);
2809 strcpy(loadmodel->name, name);
2810 // textures and memory belong to the main model
2811 loadmodel->texturepool = NULL;
2812 loadmodel->mempool = NULL;
2817 loadmodel = originalloadmodel;
2818 //Mod_Q1BSP_ProcessLightList();
2821 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2825 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2832 in = (void *)(mod_base + l->fileofs);
2833 if (l->filelen % sizeof(*in))
2834 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2835 count = l->filelen / sizeof(*in);
2836 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2839 loadmodel->num = count;
2841 for (i = 0;i < count;i++, in++, out++)
2847 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2854 in = (void *)(mod_base + l->fileofs);
2855 if (l->filelen % sizeof(*in))
2856 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2857 count = l->filelen / sizeof(*in);
2858 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2861 loadmodel->num = count;
2863 for (i = 0;i < count;i++, in++, out++)
2869 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2876 in = (void *)(mod_base + l->fileofs);
2877 if (l->filelen % sizeof(*in))
2878 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2879 count = l->filelen / sizeof(*in);
2880 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2883 loadmodel->num = count;
2885 for (i = 0;i < count;i++, in++, out++)
2891 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2898 in = (void *)(mod_base + l->fileofs);
2899 if (l->filelen % sizeof(*in))
2900 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2901 count = l->filelen / sizeof(*in);
2902 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2905 loadmodel->num = count;
2907 for (i = 0;i < count;i++, in++, out++)
2913 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2920 in = (void *)(mod_base + l->fileofs);
2921 if (l->filelen % sizeof(*in))
2922 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2923 count = l->filelen / sizeof(*in);
2924 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2927 loadmodel->num = count;
2929 for (i = 0;i < count;i++, in++, out++)
2935 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2942 in = (void *)(mod_base + l->fileofs);
2943 if (l->filelen % sizeof(*in))
2944 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2945 count = l->filelen / sizeof(*in);
2946 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2949 loadmodel->num = count;
2951 for (i = 0;i < count;i++, in++, out++)
2957 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2964 in = (void *)(mod_base + l->fileofs);
2965 if (l->filelen % sizeof(*in))
2966 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2967 count = l->filelen / sizeof(*in);
2968 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2971 loadmodel->num = count;
2973 for (i = 0;i < count;i++, in++, out++)
2979 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
2986 in = (void *)(mod_base + l->fileofs);
2987 if (l->filelen % sizeof(*in))
2988 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2989 count = l->filelen / sizeof(*in);
2990 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2993 loadmodel->num = count;
2995 for (i = 0;i < count;i++, in++, out++)
3001 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3008 in = (void *)(mod_base + l->fileofs);
3009 if (l->filelen % sizeof(*in))
3010 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3011 count = l->filelen / sizeof(*in);
3012 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3015 loadmodel->num = count;
3017 for (i = 0;i < count;i++, in++, out++)
3023 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3030 in = (void *)(mod_base + l->fileofs);
3031 if (l->filelen % sizeof(*in))
3032 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3033 count = l->filelen / sizeof(*in);
3034 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3037 loadmodel->num = count;
3039 for (i = 0;i < count;i++, in++, out++)
3045 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3052 in = (void *)(mod_base + l->fileofs);
3053 if (l->filelen % sizeof(*in))
3054 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3055 count = l->filelen / sizeof(*in);
3056 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3059 loadmodel->num = count;
3061 for (i = 0;i < count;i++, in++, out++)
3067 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3074 in = (void *)(mod_base + l->fileofs);
3075 if (l->filelen % sizeof(*in))
3076 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3077 count = l->filelen / sizeof(*in);
3078 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3081 loadmodel->num = count;
3083 for (i = 0;i < count;i++, in++, out++)
3089 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3096 in = (void *)(mod_base + l->fileofs);
3097 if (l->filelen % sizeof(*in))
3098 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3099 count = l->filelen / sizeof(*in);
3100 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3103 loadmodel->num = count;
3105 for (i = 0;i < count;i++, in++, out++)
3111 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3118 in = (void *)(mod_base + l->fileofs);
3119 if (l->filelen % sizeof(*in))
3120 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3121 count = l->filelen / sizeof(*in);
3122 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3125 loadmodel->num = count;
3127 for (i = 0;i < count;i++, in++, out++)
3133 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3140 in = (void *)(mod_base + l->fileofs);
3141 if (l->filelen % sizeof(*in))
3142 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3143 count = l->filelen / sizeof(*in);
3144 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3147 loadmodel->num = count;
3149 for (i = 0;i < count;i++, in++, out++)
3155 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3162 in = (void *)(mod_base + l->fileofs);
3163 if (l->filelen % sizeof(*in))
3164 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3165 count = l->filelen / sizeof(*in);
3166 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3169 loadmodel->num = count;
3171 for (i = 0;i < count;i++, in++, out++)
3177 static void Mod_Q2BSP_LoadModels(lump_t *l)
3184 in = (void *)(mod_base + l->fileofs);
3185 if (l->filelen % sizeof(*in))
3186 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3187 count = l->filelen / sizeof(*in);
3188 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3191 loadmodel->num = count;
3193 for (i = 0;i < count;i++, in++, out++)
3199 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3202 q2dheader_t *header;
3204 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3206 mod->type = mod_brushq2;
3208 header = (q2dheader_t *)buffer;
3210 i = LittleLong(header->version);
3211 if (i != Q2BSPVERSION)
3212 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3213 mod->brush.ishlbsp = false;
3214 if (loadmodel->isworldmodel)
3216 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3217 // until we get a texture for it...
3221 mod_base = (qbyte *)header;
3223 // swap all the lumps
3224 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3225 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3227 // store which lightmap format to use
3228 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3230 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3231 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3232 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3233 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3234 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3235 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3236 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3237 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3238 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3239 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3240 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3241 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3242 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3243 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3244 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3245 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3246 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3247 // LordHavoc: must go last because this makes the submodels
3248 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3251 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3252 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3254 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3257 char key[128], value[4096];
3259 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3260 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3261 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3264 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3265 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3266 data = loadmodel->brush.entities;
3267 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3268 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3272 if (!COM_ParseToken(&data, false))
3274 if (com_token[0] == '}')
3275 break; // end of worldspawn
3276 if (com_token[0] == '_')
3277 strcpy(key, com_token + 1);
3279 strcpy(key, com_token);
3280 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3281 key[strlen(key)-1] = 0;
3282 if (!COM_ParseToken(&data, false))
3284 strcpy(value, com_token);
3285 if (!strcmp("gridsize", key))
3287 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3288 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3294 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3300 in = (void *)(mod_base + l->fileofs);
3301 if (l->filelen % sizeof(*in))
3302 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3303 count = l->filelen / sizeof(*in);
3304 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3306 loadmodel->brushq3.data_textures = out;
3307 loadmodel->brushq3.num_textures = count;
3309 for (i = 0;i < count;i++, in++, out++)
3311 strncpy(out->name, in->name, sizeof(out->name) - 1);
3312 out->surfaceflags = LittleLong(in->surfaceflags);
3313 out->nativecontents = LittleLong(in->contents);
3314 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3315 out->renderflags = 0;
3316 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3317 out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3320 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3324 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3330 in = (void *)(mod_base + l->fileofs);
3331 if (l->filelen % sizeof(*in))
3332 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3333 count = l->filelen / sizeof(*in);
3334 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3336 loadmodel->brushq3.data_planes = out;
3337 loadmodel->brushq3.num_planes = count;
3339 for (i = 0;i < count;i++, in++, out++)
3341 out->normal[0] = LittleLong(in->normal[0]);
3342 out->normal[1] = LittleLong(in->normal[1]);
3343 out->normal[2] = LittleLong(in->normal[2]);
3344 out->dist = LittleLong(in->dist);
3349 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3352 q3mbrushside_t *out;
3355 in = (void *)(mod_base + l->fileofs);
3356 if (l->filelen % sizeof(*in))
3357 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3358 count = l->filelen / sizeof(*in);
3359 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3361 loadmodel->brushq3.data_brushsides = out;
3362 loadmodel->brushq3.num_brushsides = count;
3364 for (i = 0;i < count;i++, in++, out++)
3366 n = LittleLong(in->planeindex);
3367 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3368 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3369 out->plane = loadmodel->brushq3.data_planes + n;
3370 n = LittleLong(in->textureindex);
3371 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3372 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3373 out->texture = loadmodel->brushq3.data_textures + n;
3377 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3381 int i, j, n, c, count, maxplanes;
3384 in = (void *)(mod_base + l->fileofs);
3385 if (l->filelen % sizeof(*in))
3386 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3387 count = l->filelen / sizeof(*in);
3388 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3390 loadmodel->brushq3.data_brushes = out;
3391 loadmodel->brushq3.num_brushes = count;
3396 for (i = 0;i < count;i++, in++, out++)
3398 n = LittleLong(in->firstbrushside);
3399 c = LittleLong(in->numbrushsides);
3400 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3401 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3402 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3403 out->numbrushsides = c;
3404 n = LittleLong(in->textureindex);
3405 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3406 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3407 out->texture = loadmodel->brushq3.data_textures + n;
3409 // make a list of mplane_t structs to construct a colbrush from
3410 if (maxplanes < out->numbrushsides)
3412 maxplanes = out->numbrushsides;
3415 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3417 for (j = 0;j < out->numbrushsides;j++)
3419 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3420 planes[j].dist = out->firstbrushside[j].plane->dist;
3422 // make the colbrush from the planes
3423 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3429 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3435 in = (void *)(mod_base + l->fileofs);
3436 if (l->filelen % sizeof(*in))
3437 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3438 count = l->filelen / sizeof(*in);
3439 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3441 loadmodel->brushq3.data_effects = out;
3442 loadmodel->brushq3.num_effects = count;
3444 for (i = 0;i < count;i++, in++, out++)
3446 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3447 n = LittleLong(in->brushindex);
3448 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3449 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3450 out->brush = loadmodel->brushq3.data_brushes + n;
3451 out->unknown = LittleLong(in->unknown);
3455 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3460 in = (void *)(mod_base + l->fileofs);
3461 if (l->filelen % sizeof(*in))
3462 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3463 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3464 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3465 loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3466 loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3467 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3468 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3469 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3470 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3472 for (i = 0;i < count;i++, in++)
3474 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3475 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3476 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3477 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3478 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3479 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3480 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3481 // svector/tvector are calculated later in face loading
3482 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3483 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3484 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3485 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3486 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3487 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3488 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3489 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3490 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3491 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3492 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3493 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3494 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3498 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3504 in = (void *)(mod_base + l->fileofs);
3505 if (l->filelen % sizeof(int[3]))
3506 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3507 count = l->filelen / sizeof(*in);
3508 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3510 loadmodel->brushq3.num_triangles = count / 3;
3511 loadmodel->brushq3.data_element3i = out;
3512 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3514 for (i = 0;i < count;i++, in++, out++)
3516 *out = LittleLong(*in);
3517 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3519 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3525 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3531 in = (void *)(mod_base + l->fileofs);
3532 if (l->filelen % sizeof(*in))
3533 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3534 count = l->filelen / sizeof(*in);
3535 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3537 loadmodel->brushq3.data_lightmaps = out;
3538 loadmodel->brushq3.num_lightmaps = count;
3540 for (i = 0;i < count;i++, in++, out++)
3541 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3544 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3548 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3549 //int *originalelement3i;
3550 //int *originalneighbor3i;
3551 float *originalvertex3f;
3552 //float *originalsvector3f;
3553 //float *originaltvector3f;
3554 //float *originalnormal3f;
3555 float *originalcolor4f;
3556 float *originaltexcoordtexture2f;
3557 float *originaltexcoordlightmap2f;
3560 in = (void *)(mod_base + l->fileofs);
3561 if (l->filelen % sizeof(*in))
3562 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3563 count = l->filelen / sizeof(*in);
3564 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3566 loadmodel->brushq3.data_faces = out;
3567 loadmodel->brushq3.num_faces = count;
3569 for (i = 0;i < count;i++, in++, out++)
3571 // check face type first
3572 out->type = LittleLong(in->type);
3573 if (out->type != Q3FACETYPE_POLYGON
3574 && out->type != Q3FACETYPE_PATCH
3575 && out->type != Q3FACETYPE_MESH
3576 && out->type != Q3FACETYPE_FLARE)
3578 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3579 out->type = 0; // error
3583 n = LittleLong(in->textureindex);
3584 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3586 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3587 out->type = 0; // error
3591 out->texture = loadmodel->brushq3.data_textures + n;
3592 n = LittleLong(in->effectindex);
3593 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3595 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3601 out->effect = loadmodel->brushq3.data_effects + n;
3602 n = LittleLong(in->lightmapindex);
3603 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3605 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3609 out->lightmaptexture = NULL;
3611 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3613 out->firstvertex = LittleLong(in->firstvertex);
3614 out->numvertices = LittleLong(in->numvertices);
3615 out->firstelement = LittleLong(in->firstelement);
3616 out->numelements = LittleLong(in->numelements);
3617 out->numtriangles = out->numelements / 3;
3618 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3620 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);
3621 out->type = 0; // error
3624 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3626 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);
3627 out->type = 0; // error
3630 if (out->numtriangles * 3 != out->numelements)
3632 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3633 out->type = 0; // error
3636 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3637 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3638 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3639 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3640 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3641 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3642 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3643 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3644 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3647 case Q3FACETYPE_POLYGON:
3648 case Q3FACETYPE_MESH:
3649 // no processing necessary
3651 case Q3FACETYPE_PATCH:
3652 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3653 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3654 if (patchsize[0] < 1 || patchsize[1] < 1)
3656 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3657 out->type = 0; // error
3660 // convert patch to Q3FACETYPE_MESH
3661 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3662 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3663 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3664 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3665 finalvertices = finalwidth * finalheight;
3666 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3667 originalvertex3f = out->data_vertex3f;
3668 //originalsvector3f = out->data_svector3f;
3669 //originaltvector3f = out->data_tvector3f;
3670 //originalnormal3f = out->data_normal3f;
3671 originalcolor4f = out->data_color4f;
3672 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3673 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3674 //originalelement3i = out->data_element3i;
3675 //originalneighbor3i = out->data_neighbor3i;
3676 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
3677 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3678 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3679 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3680 out->data_color4f = out->data_normal3f + finalvertices * 3;
3681 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3682 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3683 out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
3684 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3685 out->type = Q3FACETYPE_MESH;
3686 out->firstvertex = -1;
3687 out->numvertices = finalvertices;
3688 out->firstelement = -1;
3689 out->numtriangles = finaltriangles;
3690 out->numelements = finaltriangles * 3;
3691 // generate geometry
3692 // (note: normals are skipped because they get recalculated)
3693 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3694 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3695 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3696 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3697 // generate elements
3698 e = out->data_element3i;
3699 for (y = 0;y < finalheight - 1;y++)
3701 row0 = (y + 0) * finalwidth;
3702 row1 = (y + 1) * finalwidth;
3703 for (x = 0;x < finalwidth - 1;x++)
3715 out->numtriangles = Mod_RemoveDegenerateTriangles(out->numtriangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3716 if (developer.integer)
3718 if (out->numtriangles < finaltriangles)
3719 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->numvertices, finaltriangles, finaltriangles - out->numtriangles, out->numtriangles);
3721 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->numvertices, out->numtriangles);
3723 // q3map does not put in collision brushes for curves... ugh
3724 out->collisions = true;
3726 case Q3FACETYPE_FLARE:
3727 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3729 out->numtriangles = 0;
3732 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3733 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3735 if (invalidelements)
3737 Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->numvertices, out->firstelement, out->numelements);
3738 for (j = 0;j < out->numelements;j++)
3740 Con_Printf(" %i", out->data_element3i[j]);
3741 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3742 out->data_element3i[j] = 0;
3746 // for shadow volumes
3747 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->numtriangles);
3748 // for per pixel lighting
3749 Mod_BuildTextureVectorsAndNormals(out->numvertices, out->numtriangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
3750 // calculate a bounding box
3751 VectorClear(out->mins);
3752 VectorClear(out->maxs);
3753 if (out->numvertices)
3755 VectorCopy(out->data_vertex3f, out->mins);
3756 VectorCopy(out->data_vertex3f, out->maxs);
3757 for (j = 1, v = out->data_vertex3f + 3;j < out->numvertices;j++, v += 3)
3759 out->mins[0] = min(out->mins[0], v[0]);
3760 out->maxs[0] = max(out->maxs[0], v[0]);
3761 out->mins[1] = min(out->mins[1], v[1]);
3762 out->maxs[1] = max(out->maxs[1], v[1]);
3763 out->mins[2] = min(out->mins[2], v[2]);
3764 out->maxs[2] = max(out->maxs[2], v[2]);
3766 out->mins[0] -= 1.0f;
3767 out->mins[1] -= 1.0f;
3768 out->mins[2] -= 1.0f;
3769 out->maxs[0] += 1.0f;
3770 out->maxs[1] += 1.0f;
3771 out->maxs[2] += 1.0f;
3776 static void Mod_Q3BSP_LoadModels(lump_t *l)
3780 int i, j, n, c, count;
3782 in = (void *)(mod_base + l->fileofs);
3783 if (l->filelen % sizeof(*in))
3784 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3785 count = l->filelen / sizeof(*in);
3786 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3788 loadmodel->brushq3.data_models = out;
3789 loadmodel->brushq3.num_models = count;
3791 for (i = 0;i < count;i++, in++, out++)
3793 for (j = 0;j < 3;j++)
3795 out->mins[j] = LittleFloat(in->mins[j]);
3796 out->maxs[j] = LittleFloat(in->maxs[j]);
3798 n = LittleLong(in->firstface);
3799 c = LittleLong(in->numfaces);
3800 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3801 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3802 out->firstface = loadmodel->brushq3.data_faces + n;
3804 n = LittleLong(in->firstbrush);
3805 c = LittleLong(in->numbrushes);
3806 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3807 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3808 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3809 out->numbrushes = c;
3813 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3819 in = (void *)(mod_base + l->fileofs);
3820 if (l->filelen % sizeof(*in))
3821 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3822 count = l->filelen / sizeof(*in);
3823 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3825 loadmodel->brushq3.data_leafbrushes = out;
3826 loadmodel->brushq3.num_leafbrushes = count;
3828 for (i = 0;i < count;i++, in++, out++)
3830 n = LittleLong(*in);
3831 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3832 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3833 *out = loadmodel->brushq3.data_brushes + n;
3837 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3843 in = (void *)(mod_base + l->fileofs);
3844 if (l->filelen % sizeof(*in))
3845 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3846 count = l->filelen / sizeof(*in);
3847 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3849 loadmodel->brushq3.data_leaffaces = out;
3850 loadmodel->brushq3.num_leaffaces = count;
3852 for (i = 0;i < count;i++, in++, out++)
3854 n = LittleLong(*in);
3855 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3856 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3857 *out = loadmodel->brushq3.data_faces + n;
3861 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3865 int i, j, n, c, count;
3867 in = (void *)(mod_base + l->fileofs);
3868 if (l->filelen % sizeof(*in))
3869 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3870 count = l->filelen / sizeof(*in);
3871 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3873 loadmodel->brushq3.data_leafs = out;
3874 loadmodel->brushq3.num_leafs = count;
3876 for (i = 0;i < count;i++, in++, out++)
3878 out->isnode = false;
3880 out->clusterindex = LittleLong(in->clusterindex);
3881 out->areaindex = LittleLong(in->areaindex);
3882 for (j = 0;j < 3;j++)
3884 // yes the mins/maxs are ints
3885 out->mins[j] = LittleLong(in->mins[j]);
3886 out->maxs[j] = LittleLong(in->maxs[j]);
3888 n = LittleLong(in->firstleafface);
3889 c = LittleLong(in->numleaffaces);
3890 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3891 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3892 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3893 out->numleaffaces = c;
3894 n = LittleLong(in->firstleafbrush);
3895 c = LittleLong(in->numleafbrushes);
3896 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3897 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3898 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3899 out->numleafbrushes = c;
3903 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3906 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3907 node->parent = parent;
3910 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3911 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3915 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3921 in = (void *)(mod_base + l->fileofs);
3922 if (l->filelen % sizeof(*in))
3923 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3924 count = l->filelen / sizeof(*in);
3925 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3927 loadmodel->brushq3.data_nodes = out;
3928 loadmodel->brushq3.num_nodes = count;
3930 for (i = 0;i < count;i++, in++, out++)
3934 n = LittleLong(in->planeindex);
3935 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3936 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3937 out->plane = loadmodel->brushq3.data_planes + n;
3938 for (j = 0;j < 2;j++)
3940 n = LittleLong(in->childrenindex[j]);
3943 if (n >= loadmodel->brushq3.num_nodes)
3944 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3945 out->children[j] = loadmodel->brushq3.data_nodes + n;
3950 if (n >= loadmodel->brushq3.num_leafs)
3951 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3952 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3955 for (j = 0;j < 3;j++)
3957 // yes the mins/maxs are ints
3958 out->mins[j] = LittleLong(in->mins[j]);
3959 out->maxs[j] = LittleLong(in->maxs[j]);
3963 // set the parent pointers
3964 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3967 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3970 q3dlightgrid_t *out;
3973 in = (void *)(mod_base + l->fileofs);
3974 if (l->filelen % sizeof(*in))
3975 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
3976 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
3977 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
3978 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
3979 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3980 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3981 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3982 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3983 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3984 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3985 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
3986 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
3987 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
3988 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
3989 if (l->filelen < count * (int)sizeof(*in))
3990 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]);
3991 if (l->filelen != count * (int)sizeof(*in))
3992 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
3994 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3995 loadmodel->brushq3.data_lightgrid = out;
3996 loadmodel->brushq3.num_lightgrid = count;
3998 // no swapping or validation necessary
3999 memcpy(out, in, count * (int)sizeof(*out));
4001 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]);
4002 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]);
4005 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4010 if (l->filelen == 0)
4013 in = (void *)(mod_base + l->fileofs);
4015 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4017 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4018 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4019 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4020 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4021 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4022 if (l->filelen < totalchains + (int)sizeof(*in))
4023 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);
4025 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4026 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4029 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4031 // FIXME: finish this code
4032 VectorCopy(in, out);
4035 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4037 int i, j, k, index[3];
4038 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4039 q3dlightgrid_t *a, *s;
4040 // FIXME: write this
4041 if (!model->brushq3.num_lightgrid)
4043 ambientcolor[0] += 128;
4044 ambientcolor[1] += 128;
4045 ambientcolor[2] += 128;
4048 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4049 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4050 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4051 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0]);
4052 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1]);
4053 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2]);
4054 index[0] = (int)floor(transformed[0]);
4055 index[1] = (int)floor(transformed[1]);
4056 index[2] = (int)floor(transformed[2]);
4057 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4058 // now lerp the values
4059 VectorClear(diffusenormal);
4060 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4061 for (k = 0;k < 2;k++)
4063 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4064 for (j = 0;j < 2;j++)
4066 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4067 for (i = 0;i < 2;i++)
4069 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4070 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4071 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4072 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4073 pitch = s->diffusepitch * M_PI / 128;
4074 yaw = s->diffuseyaw * M_PI / 128;
4075 sinpitch = sin(pitch);
4076 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4077 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4078 diffusenormal[2] += blend * (cos(pitch));
4079 //Con_Printf("blend %f: ambient %i %i %i, diffuse %i %i %i, diffusepitch %i diffuseyaw %i (%f %f, normal %f %f %f)\n", blend, s->ambientrgb[0], s->ambientrgb[1], s->ambientrgb[2], s->diffusergb[0], s->diffusergb[1], s->diffusergb[2], s->diffusepitch, s->diffuseyaw, pitch, yaw, (cos(yaw) * cospitch), (sin(yaw) * cospitch), (-sin(pitch)));
4083 VectorNormalize(diffusenormal);
4084 //Con_Printf("result: ambient %f %f %f diffuse %f %f %f diffusenormal %f %f %f\n", ambientcolor[0], ambientcolor[1], ambientcolor[2], diffusecolor[0], diffusecolor[1], diffusecolor[2], diffusenormal[0], diffusenormal[1], diffusenormal[2]);
4087 static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t start, const vec3_t end, vec_t startfrac, vec_t endfrac, const vec3_t linestart, const vec3_t lineend, int markframe)
4089 int i, startside, endside;
4090 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4094 if (startfrac >= trace->fraction)
4096 // note: all line fragments past first impact fraction are ignored
4097 while (node->isnode)
4099 // recurse down node sides
4100 dist1 = PlaneDiff(start, node->plane);
4101 dist2 = PlaneDiff(end, node->plane);
4102 startside = dist1 < 0;
4103 endside = dist2 < 0;
4104 if (startside == endside)
4106 // most of the time the line fragment is on one side of the plane
4107 node = node->children[startside];
4111 // line crosses node plane, split the line
4112 midfrac = dist1 / (dist1 - dist2);
4113 VectorLerp(linestart, midfrac, lineend, mid);
4114 // take the near side first
4115 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4116 if (midfrac < trace->fraction)
4117 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4122 segmentmins[0] = min(start[0], end[0]);
4123 segmentmins[1] = min(start[1], end[1]);
4124 segmentmins[2] = min(start[2], end[2]);
4125 segmentmaxs[0] = max(start[0], end[0]);
4126 segmentmaxs[1] = max(start[1], end[1]);
4127 segmentmaxs[2] = max(start[2], end[2]);
4128 leaf = (q3mleaf_t *)node;
4129 for (i = 0;i < leaf->numleafbrushes;i++)
4131 if (startfrac >= trace->fraction)
4133 brush = leaf->firstleafbrush[i]->colbrushf;
4134 if (brush && brush->markframe != markframe)
4136 brush->markframe = markframe;
4137 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4138 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4141 if (mod_q3bsp_curves_collisions.integer)
4143 for (i = 0;i < leaf->numleaffaces;i++)
4145 if (startfrac >= trace->fraction)
4147 face = leaf->firstleafface[i];
4148 if (face->collisions && face->collisionmarkframe != markframe)
4150 face->collisionmarkframe = markframe;
4151 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4152 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4158 static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs)
4161 float nodesegmentmins[3], nodesegmentmaxs[3];
4165 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4166 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4167 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4168 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4169 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4170 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4171 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4175 // recurse down node sides
4176 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4179 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4180 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4182 else if (sides == 2)
4183 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4185 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4187 dist = node->plane->dist - (1.0f / 8.0f);
4188 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4190 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4192 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4198 dist = node->plane->dist + (1.0f / 8.0f);
4199 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4201 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4203 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4209 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4211 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4213 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4219 leaf = (q3mleaf_t *)node;
4220 for (i = 0;i < leaf->numleafbrushes;i++)
4222 brush = leaf->firstleafbrush[i]->colbrushf;
4223 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4225 brush->markframe = markframe;
4226 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4229 if (mod_q3bsp_curves_collisions.integer)
4231 for (i = 0;i < leaf->numleaffaces;i++)
4233 face = leaf->firstleafface[i];
4234 // note: this can not be optimized with a face->collisionmarkframe because each triangle of the face would need to be marked as done individually (because each one is bbox culled individually), and if all are marked, then the face could be marked as done
4235 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4236 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4242 static 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, int hitsupercontentsmask)
4245 float segmentmins[3], segmentmaxs[3];
4246 colbrushf_t *thisbrush_start, *thisbrush_end;
4247 matrix4x4_t startmatrix, endmatrix;
4248 static int markframe = 0;
4250 memset(trace, 0, sizeof(*trace));
4251 trace->fraction = 1;
4252 trace->hitsupercontentsmask = hitsupercontentsmask;
4253 Matrix4x4_CreateIdentity(&startmatrix);
4254 Matrix4x4_CreateIdentity(&endmatrix);
4255 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4256 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4257 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4258 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4259 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4260 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4261 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4264 if (model->brushq3.submodel)
4266 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4267 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4268 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4269 if (mod_q3bsp_curves_collisions.integer)
4271 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4273 face = model->brushq3.data_thismodel->firstface + i;
4274 if (face->collisions)
4275 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4280 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4284 // box trace, performed as brush trace
4285 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4286 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4287 if (model->brushq3.submodel)
4289 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4290 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4291 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4292 if (mod_q3bsp_curves_collisions.integer)
4294 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4296 face = model->brushq3.data_thismodel->firstface + i;
4297 if (face->collisions)
4298 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4303 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4308 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)
4315 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4316 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4319 // node - recurse down the BSP tree
4320 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4323 node = node->children[0];
4326 node = node->children[1];
4328 default: // crossing
4329 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4331 node = node->children[1];
4338 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4340 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4343 //Returns PVS data for a given point
4344 //(note: can return NULL)
4345 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4348 Mod_CheckLoaded(model);
4349 node = model->brushq3.data_nodes;
4350 while (node->isnode)
4351 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4352 if (((q3mleaf_t *)node)->clusterindex >= 0)
4353 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4358 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4364 while (node->isnode)
4366 d = PlaneDiff(org, node->plane);
4368 node = node->children[0];
4369 else if (d < -radius)
4370 node = node->children[1];
4373 // go down both sides
4374 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4375 node = node->children[1];
4378 // if this is a leaf with a pvs, accumulate the pvs bits
4379 if (((q3mleaf_t *)node)->clusterindex >= 0)
4381 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4382 for (i = 0;i < pvsbytes;i++)
4383 pvsbuffer[i] |= pvs[i];
4388 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4389 //of the given point.
4390 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4392 int bytes = model->brushq3.num_pvschainlength;
4393 bytes = min(bytes, pvsbufferlength);
4394 memset(pvsbuffer, 0, bytes);
4395 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4400 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4402 int supercontents = 0;
4403 if (nativecontents & Q2CONTENTS_SOLID)
4404 supercontents |= SUPERCONTENTS_SOLID;
4405 if (nativecontents & Q2CONTENTS_WATER)
4406 supercontents |= SUPERCONTENTS_WATER;
4407 if (nativecontents & Q2CONTENTS_SLIME)
4408 supercontents |= SUPERCONTENTS_SLIME;
4409 if (nativecontents & Q2CONTENTS_LAVA)
4410 supercontents |= SUPERCONTENTS_LAVA;
4411 return supercontents;
4414 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4416 int nativecontents = 0;
4417 if (supercontents & SUPERCONTENTS_SOLID)
4418 nativecontents |= Q2CONTENTS_SOLID;
4419 if (supercontents & SUPERCONTENTS_WATER)
4420 nativecontents |= Q2CONTENTS_WATER;
4421 if (supercontents & SUPERCONTENTS_SLIME)
4422 nativecontents |= Q2CONTENTS_SLIME;
4423 if (supercontents & SUPERCONTENTS_LAVA)
4424 nativecontents |= Q2CONTENTS_LAVA;
4425 return nativecontents;
4428 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4429 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4430 //extern void R_Q3BSP_DrawFakeShadow(struct entity_render_s *ent);
4431 //extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4432 //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);
4433 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4436 q3dheader_t *header;
4437 float corner[3], yawradius, modelradius;
4439 mod->type = mod_brushq3;
4443 header = (q3dheader_t *)buffer;
4445 i = LittleLong(header->version);
4446 if (i != Q3BSPVERSION)
4447 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4448 if (loadmodel->isworldmodel)
4450 Cvar_SetValue("halflifebsp", false);
4451 // until we get a texture for it...
4455 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4456 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4457 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4458 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4459 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4460 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4461 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4462 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4463 //mod->DrawSky = R_Q3BSP_DrawSky;
4464 mod->Draw = R_Q3BSP_Draw;
4465 //mod->DrawFakeShadow = R_Q3BSP_DrawFakeShadow;
4466 //mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4467 //mod->DrawLight = R_Q3BSP_DrawLight;
4469 mod_base = (qbyte *)header;
4471 // swap all the lumps
4472 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4473 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4475 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4476 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4477 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4478 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4479 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4480 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4481 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4482 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4483 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4484 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4485 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4486 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4487 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4488 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4489 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4490 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4491 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4492 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4494 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4501 // LordHavoc: only register submodels if it is the world
4502 // (prevents bsp models from replacing world submodels)
4503 if (!loadmodel->isworldmodel)
4505 // duplicate the basic information
4506 sprintf(name, "*%i", i);
4507 mod = Mod_FindName(name);
4509 strcpy(mod->name, name);
4510 // textures and memory belong to the main model
4511 mod->texturepool = NULL;
4512 mod->mempool = NULL;
4514 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4515 mod->brushq3.submodel = i;
4517 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4518 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4519 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4520 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4521 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4522 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4523 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4524 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4525 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4526 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4527 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4528 mod->yawmins[2] = mod->normalmins[2];
4529 mod->yawmaxs[2] = mod->normalmaxs[2];
4530 mod->radius = modelradius;
4531 mod->radius2 = modelradius * modelradius;
4535 void Mod_IBSP_Load(model_t *mod, void *buffer)
4537 int i = LittleLong(((int *)buffer)[1]);
4538 if (i == Q3BSPVERSION)
4539 Mod_Q3BSP_Load(mod,buffer);
4540 else if (i == Q2BSPVERSION)
4541 Mod_Q2BSP_Load(mod,buffer);
4543 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4546 void Mod_MAP_Load(model_t *mod, void *buffer)
4548 Host_Error("Mod_MAP_Load: not yet implemented\n");