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", "0"};
41 static void Mod_Q1BSP_Collision_Init (void);
42 void Mod_BrushInit(void)
44 // Cvar_RegisterVariable(&r_subdivide_size);
45 Cvar_RegisterVariable(&halflifebsp);
46 Cvar_RegisterVariable(&r_novis);
47 Cvar_RegisterVariable(&r_miplightmaps);
48 Cvar_RegisterVariable(&r_lightmaprgba);
49 Cvar_RegisterVariable(&r_nosurftextures);
50 Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
51 Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
52 Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
53 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
54 Mod_Q1BSP_Collision_Init();
57 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
64 Mod_CheckLoaded(model);
66 // LordHavoc: modified to start at first clip node,
67 // in other words: first node of the (sub)model
68 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
69 while (node->contents == 0)
70 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
72 return (mleaf_t *)node;
75 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
79 leaf = Mod_Q1BSP_PointInLeaf(model, p);
82 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
85 memcpy(out, leaf->ambient_sound_level, i);
91 memset(out, 0, outsize);
95 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)
99 if (node->contents < 0)
102 if (node->contents == CONTENTS_SOLID)
104 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
105 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
108 // node - recurse down the BSP tree
109 switch (BoxOnPlaneSide(mins, maxs, node->plane))
112 node = node->children[0];
115 node = node->children[1];
118 if (node->children[0]->contents != CONTENTS_SOLID)
119 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
121 node = node->children[1];
128 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
130 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
134 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
139 return CONTENTS_EMPTY;
141 Mod_CheckLoaded(model);
143 // LordHavoc: modified to start at first clip node,
144 // in other words: first node of the (sub)model
145 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
146 while (node->contents == 0)
147 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
149 return ((mleaf_t *)node)->contents;
153 typedef struct findnonsolidlocationinfo_s
161 findnonsolidlocationinfo_t;
164 extern cvar_t samelevel;
166 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
168 int i, surfnum, k, *tri, *mark;
169 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
174 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
176 surf = info->model->brushq1.surfaces + *mark;
177 if (surf->flags & SURF_SOLIDCLIP)
180 VectorCopy(surf->plane->normal, surfnormal);
181 if (surf->flags & SURF_PLANEBACK)
182 VectorNegate(surfnormal, surfnormal);
184 for (k = 0;k < surf->mesh.num_triangles;k++)
186 tri = surf->mesh.data_element3i + k * 3;
187 VectorCopy((surf->mesh.data_vertex3f + tri[0] * 3), vert[0]);
188 VectorCopy((surf->mesh.data_vertex3f + tri[1] * 3), vert[1]);
189 VectorCopy((surf->mesh.data_vertex3f + tri[2] * 3), vert[2]);
190 VectorSubtract(vert[1], vert[0], edge[0]);
191 VectorSubtract(vert[2], vert[1], edge[1]);
192 CrossProduct(edge[1], edge[0], facenormal);
193 if (facenormal[0] || facenormal[1] || facenormal[2])
195 VectorNormalize(facenormal);
197 if (VectorDistance(facenormal, surfnormal) > 0.01f)
198 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
200 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
201 if (f <= info->bestdist && f >= -info->bestdist)
203 VectorSubtract(vert[0], vert[2], edge[2]);
204 VectorNormalize(edge[0]);
205 VectorNormalize(edge[1]);
206 VectorNormalize(edge[2]);
207 CrossProduct(facenormal, edge[0], edgenormal[0]);
208 CrossProduct(facenormal, edge[1], edgenormal[1]);
209 CrossProduct(facenormal, edge[2], edgenormal[2]);
211 if (samelevel.integer & 1)
212 VectorNegate(edgenormal[0], edgenormal[0]);
213 if (samelevel.integer & 2)
214 VectorNegate(edgenormal[1], edgenormal[1]);
215 if (samelevel.integer & 4)
216 VectorNegate(edgenormal[2], edgenormal[2]);
217 for (i = 0;i < 3;i++)
218 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
219 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
220 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
221 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]);
224 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
225 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
226 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
228 // we got lucky, the center is within the face
229 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
233 if (info->bestdist > dist)
235 info->bestdist = dist;
236 VectorScale(facenormal, (info->radius - -dist), info->nudge);
241 if (info->bestdist > dist)
243 info->bestdist = dist;
244 VectorScale(facenormal, (info->radius - dist), info->nudge);
250 // check which edge or vertex the center is nearest
251 for (i = 0;i < 3;i++)
253 f = DotProduct(info->center, edge[i]);
254 if (f >= DotProduct(vert[0], edge[i])
255 && f <= DotProduct(vert[1], edge[i]))
258 VectorMA(info->center, -f, edge[i], point);
259 dist = sqrt(DotProduct(point, point));
260 if (info->bestdist > dist)
262 info->bestdist = dist;
263 VectorScale(point, (info->radius / dist), info->nudge);
265 // skip both vertex checks
266 // (both are further away than this edge)
271 // not on edge, check first vertex of edge
272 VectorSubtract(info->center, vert[i], point);
273 dist = sqrt(DotProduct(point, point));
274 if (info->bestdist > dist)
276 info->bestdist = dist;
277 VectorScale(point, (info->radius / dist), info->nudge);
289 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
293 if (((mleaf_t *)node)->nummarksurfaces)
294 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
298 float f = PlaneDiff(info->center, node->plane);
299 if (f >= -info->bestdist)
300 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
301 if (f <= info->bestdist)
302 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
306 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
309 findnonsolidlocationinfo_t info;
315 VectorCopy(in, info.center);
316 info.radius = radius;
321 VectorClear(info.nudge);
322 info.bestdist = radius;
323 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
324 VectorAdd(info.center, info.nudge, info.center);
326 while (info.bestdist < radius && ++i < 10);
327 VectorCopy(info.center, out);
330 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
332 switch(nativecontents)
337 return SUPERCONTENTS_SOLID;
339 return SUPERCONTENTS_WATER;
341 return SUPERCONTENTS_SLIME;
343 return SUPERCONTENTS_LAVA;
345 return SUPERCONTENTS_SKY;
350 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
352 if (supercontents & SUPERCONTENTS_SOLID)
353 return CONTENTS_SOLID;
354 if (supercontents & SUPERCONTENTS_SKY)
356 if (supercontents & SUPERCONTENTS_LAVA)
357 return CONTENTS_LAVA;
358 if (supercontents & SUPERCONTENTS_SLIME)
359 return CONTENTS_SLIME;
360 if (supercontents & SUPERCONTENTS_WATER)
361 return CONTENTS_WATER;
362 return CONTENTS_EMPTY;
367 // the hull we're tracing through
370 // the trace structure to fill in
373 // start, end, and end - start (in model space)
378 RecursiveHullCheckTraceInfo_t;
380 // 1/32 epsilon to keep floating point happy
381 #define DIST_EPSILON (0.03125)
383 #define HULLCHECKSTATE_EMPTY 0
384 #define HULLCHECKSTATE_SOLID 1
385 #define HULLCHECKSTATE_DONE 2
387 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
389 // status variables, these don't need to be saved on the stack when
390 // recursing... but are because this should be thread-safe
391 // (note: tracing against a bbox is not thread-safe, yet)
396 // variables that need to be stored on the stack when recursing
401 // LordHavoc: a goto! everyone flee in terror... :)
406 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
407 if (!t->trace->startfound)
409 t->trace->startfound = true;
410 t->trace->startsupercontents |= num;
412 if (num & SUPERCONTENTS_LIQUIDSMASK)
413 t->trace->inwater = true;
415 t->trace->inopen = true;
416 if (num & t->trace->hitsupercontentsmask)
418 // if the first leaf is solid, set startsolid
419 if (t->trace->allsolid)
420 t->trace->startsolid = true;
421 #if COLLISIONPARANOID >= 3
424 return HULLCHECKSTATE_SOLID;
428 t->trace->allsolid = false;
429 #if COLLISIONPARANOID >= 3
432 return HULLCHECKSTATE_EMPTY;
436 // find the point distances
437 node = t->hull->clipnodes + num;
439 plane = t->hull->planes + node->planenum;
442 t1 = p1[plane->type] - plane->dist;
443 t2 = p2[plane->type] - plane->dist;
447 t1 = DotProduct (plane->normal, p1) - plane->dist;
448 t2 = DotProduct (plane->normal, p2) - plane->dist;
455 #if COLLISIONPARANOID >= 3
458 num = node->children[1];
467 #if COLLISIONPARANOID >= 3
470 num = node->children[0];
476 // the line intersects, find intersection point
477 // LordHavoc: this uses the original trace for maximum accuracy
478 #if COLLISIONPARANOID >= 3
483 t1 = t->start[plane->type] - plane->dist;
484 t2 = t->end[plane->type] - plane->dist;
488 t1 = DotProduct (plane->normal, t->start) - plane->dist;
489 t2 = DotProduct (plane->normal, t->end) - plane->dist;
492 midf = t1 / (t1 - t2);
493 midf = bound(p1f, midf, p2f);
494 VectorMA(t->start, midf, t->dist, mid);
496 // recurse both sides, front side first
497 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
498 // if this side is not empty, return what it is (solid or done)
499 if (ret != HULLCHECKSTATE_EMPTY)
502 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
503 // if other side is not solid, return what it is (empty or done)
504 if (ret != HULLCHECKSTATE_SOLID)
507 // front is air and back is solid, this is the impact point...
510 t->trace->plane.dist = -plane->dist;
511 VectorNegate (plane->normal, t->trace->plane.normal);
515 t->trace->plane.dist = plane->dist;
516 VectorCopy (plane->normal, t->trace->plane.normal);
519 // bias away from surface a bit
520 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
521 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
523 midf = t1 / (t1 - t2);
524 t->trace->fraction = bound(0.0f, midf, 1.0);
526 #if COLLISIONPARANOID >= 3
529 return HULLCHECKSTATE_DONE;
532 #if COLLISIONPARANOID < 2
533 static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
536 num = t->hull->clipnodes[num].children[(t->hull->planes[t->hull->clipnodes[num].planenum].type < 3 ? t->start[t->hull->planes[t->hull->clipnodes[num].planenum].type] : DotProduct(t->hull->planes[t->hull->clipnodes[num].planenum].normal, t->start)) < t->hull->planes[t->hull->clipnodes[num].planenum].dist];
537 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
538 t->trace->startsupercontents |= num;
539 if (num & SUPERCONTENTS_LIQUIDSMASK)
540 t->trace->inwater = true;
542 t->trace->inopen = true;
543 if (num & t->trace->hitsupercontentsmask)
545 t->trace->allsolid = t->trace->startsolid = true;
546 return HULLCHECKSTATE_SOLID;
550 t->trace->allsolid = t->trace->startsolid = false;
551 return HULLCHECKSTATE_EMPTY;
556 static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
558 // this function currently only supports same size start and end
560 RecursiveHullCheckTraceInfo_t rhc;
562 memset(&rhc, 0, sizeof(rhc));
563 memset(trace, 0, sizeof(trace_t));
565 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
566 rhc.trace->fraction = 1;
567 rhc.trace->allsolid = true;
568 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
570 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
571 else if (model->brush.ishlbsp)
573 // LordHavoc: this has to have a minor tolerance (the .1) because of
574 // minor float precision errors from the box being transformed around
575 if (boxsize[0] < 32.1)
577 if (boxsize[2] < 54) // pick the nearest of 36 or 72
578 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
580 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
583 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
587 // LordHavoc: this has to have a minor tolerance (the .1) because of
588 // minor float precision errors from the box being transformed around
589 if (boxsize[0] < 32.1)
590 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
592 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
594 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
595 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
596 VectorSubtract(rhc.end, rhc.start, rhc.dist);
597 #if COLLISIONPARANOID >= 2
598 Con_Printf("t(%f %f %f,%f %f %f,%i %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]);
599 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
602 if (DotProduct(rhc.dist, rhc.dist))
603 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
605 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
609 static hull_t box_hull;
610 static dclipnode_t box_clipnodes[6];
611 static mplane_t box_planes[6];
613 static void Mod_Q1BSP_Collision_Init (void)
618 //Set up the planes and clipnodes so that the six floats of a bounding box
619 //can just be stored out and get a proper hull_t structure.
621 box_hull.clipnodes = box_clipnodes;
622 box_hull.planes = box_planes;
623 box_hull.firstclipnode = 0;
624 box_hull.lastclipnode = 5;
626 for (i = 0;i < 6;i++)
628 box_clipnodes[i].planenum = i;
632 box_clipnodes[i].children[side] = CONTENTS_EMPTY;
634 box_clipnodes[i].children[side^1] = i + 1;
636 box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
638 box_planes[i].type = i>>1;
639 box_planes[i].normal[i>>1] = 1;
643 void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask, int boxsupercontents)
647 colplanef_t cbox_planes[6];
648 cbox.supercontents = boxsupercontents;
651 cbox.numtriangles = 0;
652 cbox.planes = cbox_planes;
654 cbox.elements = NULL;
662 cbox_planes[0].normal[0] = 1;cbox_planes[0].normal[1] = 0;cbox_planes[0].normal[2] = 0;cbox_planes[0].dist = cmaxs[0] - mins[0];
663 cbox_planes[1].normal[0] = -1;cbox_planes[1].normal[1] = 0;cbox_planes[1].normal[2] = 0;cbox_planes[1].dist = maxs[0] - cmins[0];
664 cbox_planes[2].normal[0] = 0;cbox_planes[2].normal[1] = 1;cbox_planes[2].normal[2] = 0;cbox_planes[2].dist = cmaxs[1] - mins[1];
665 cbox_planes[3].normal[0] = 0;cbox_planes[3].normal[1] = -1;cbox_planes[3].normal[2] = 0;cbox_planes[3].dist = maxs[1] - cmins[1];
666 cbox_planes[4].normal[0] = 0;cbox_planes[4].normal[1] = 0;cbox_planes[4].normal[2] = 1;cbox_planes[4].dist = cmaxs[2] - mins[2];
667 cbox_planes[5].normal[0] = 0;cbox_planes[5].normal[1] = 0;cbox_planes[5].normal[2] = -1;cbox_planes[5].dist = maxs[2] - cmins[2];
668 memset(trace, 0, sizeof(trace_t));
669 trace->hitsupercontentsmask = hitsupercontentsmask;
671 Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox);
673 RecursiveHullCheckTraceInfo_t rhc;
674 // fill in a default trace
675 memset(&rhc, 0, sizeof(rhc));
676 memset(trace, 0, sizeof(trace_t));
677 //To keep everything totally uniform, bounding boxes are turned into small
678 //BSP trees instead of being compared directly.
679 // create a temp hull from bounding box sizes
680 box_planes[0].dist = cmaxs[0] - mins[0];
681 box_planes[1].dist = cmins[0] - maxs[0];
682 box_planes[2].dist = cmaxs[1] - mins[1];
683 box_planes[3].dist = cmins[1] - maxs[1];
684 box_planes[4].dist = cmaxs[2] - mins[2];
685 box_planes[5].dist = cmins[2] - maxs[2];
686 Con_Printf("box_planes %f:%f %f:%f %f:%f\ncbox %f %f %f:%f %f %f\nbox %f %f %f:%f %f %f\n", box_planes[0].dist, box_planes[1].dist, box_planes[2].dist, box_planes[3].dist, box_planes[4].dist, box_planes[5].dist, cmins[0], cmins[1], cmins[2], cmaxs[0], cmaxs[1], cmaxs[2], mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
687 // trace a line through the generated clipping hull
688 //rhc.boxsupercontents = boxsupercontents;
689 rhc.hull = &box_hull;
691 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
692 rhc.trace->fraction = 1;
693 rhc.trace->allsolid = true;
694 VectorCopy(start, rhc.start);
695 VectorCopy(end, rhc.end);
696 VectorSubtract(rhc.end, rhc.start, rhc.dist);
697 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
698 VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
699 if (rhc.trace->startsupercontents)
700 rhc.trace->startsupercontents = boxsupercontents;
704 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)
706 int side, distz = endz - startz;
711 if (node->contents < 0)
712 return false; // didn't hit anything
714 switch (node->plane->type)
717 node = node->children[x < node->plane->dist];
720 node = node->children[y < node->plane->dist];
723 side = startz < node->plane->dist;
724 if ((endz < node->plane->dist) == side)
726 node = node->children[side];
729 // found an intersection
730 mid = node->plane->dist;
733 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
734 front += startz * node->plane->normal[2];
735 back += endz * node->plane->normal[2];
736 side = front < node->plane->dist;
737 if ((back < node->plane->dist) == side)
739 node = node->children[side];
742 // found an intersection
743 mid = startz + distz * (front - node->plane->dist) / (front - back);
747 // go down front side
748 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
749 return true; // hit something
752 // check for impact on this node
753 if (node->numsurfaces)
758 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
759 for (i = 0;i < node->numsurfaces;i++, surf++)
761 if (!(surf->flags & SURF_LIGHTMAP))
762 continue; // no lightmaps
764 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]);
765 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]);
767 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
770 ds -= surf->texturemins[0];
771 dt -= surf->texturemins[1];
773 if (ds > surf->extents[0] || dt > surf->extents[1])
779 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;
780 line3 = ((surf->extents[0]>>4)+1)*3;
781 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
783 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
785 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
787 scale = d_lightstylevalue[surf->styles[maps]];
788 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
789 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
790 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
791 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
796 LordHavoc: here's the readable version of the interpolation
797 code, not quite as easy for the compiler to optimize...
799 dsfrac is the X position in the lightmap pixel, * 16
800 dtfrac is the Y position in the lightmap pixel, * 16
801 r00 is top left corner, r01 is top right corner
802 r10 is bottom left corner, r11 is bottom right corner
803 g and b are the same layout.
804 r0 and r1 are the top and bottom intermediate results
806 first we interpolate the top two points, to get the top
809 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
810 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
811 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
813 then we interpolate the bottom two points, to get the
816 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
817 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
818 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
820 then we interpolate the top and bottom samples to get the
821 middle sample (the one which was requested)
823 r = (((r1-r0) * dtfrac) >> 4) + r0;
824 g = (((g1-g0) * dtfrac) >> 4) + g0;
825 b = (((b1-b0) * dtfrac) >> 4) + b0;
828 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
829 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
830 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
832 return true; // success
837 node = node->children[side ^ 1];
839 distz = endz - startz;
844 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
846 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);
849 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
856 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
866 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
869 for (c = *in++;c > 0;c--)
873 Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\"\n", loadmodel->name);
882 static void Mod_Q1BSP_LoadTextures(lump_t *l)
884 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
886 texture_t *tx, *tx2, *anims[10], *altanims[10];
888 qbyte *data, *mtdata;
891 loadmodel->brushq1.textures = NULL;
893 // add two slots for notexture walls and notexture liquids
896 m = (dmiptexlump_t *)(mod_base + l->fileofs);
897 m->nummiptex = LittleLong (m->nummiptex);
898 loadmodel->brushq1.numtextures = m->nummiptex + 2;
903 loadmodel->brushq1.numtextures = 2;
906 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
908 // fill out all slots with notexture
909 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
912 strcpy(tx->name, "NO TEXTURE FOUND");
915 tx->skin.base = r_notexture;
916 tx->shader = &Cshader_wall_lightmap;
917 tx->flags = SURF_SOLIDCLIP;
918 if (i == loadmodel->brushq1.numtextures - 1)
920 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
921 tx->shader = &Cshader_water;
923 tx->currentframe = tx;
929 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
931 // LordHavoc: mostly rewritten map texture loader
932 for (i = 0;i < m->nummiptex;i++)
934 dofs[i] = LittleLong(dofs[i]);
935 if (dofs[i] == -1 || r_nosurftextures.integer)
937 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
939 // make sure name is no more than 15 characters
940 for (j = 0;dmiptex->name[j] && j < 15;j++)
941 name[j] = dmiptex->name[j];
944 mtwidth = LittleLong(dmiptex->width);
945 mtheight = LittleLong(dmiptex->height);
947 j = LittleLong(dmiptex->offsets[0]);
951 if (j < 40 || j + mtwidth * mtheight > l->filelen)
953 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
956 mtdata = (qbyte *)dmiptex + j;
959 if ((mtwidth & 15) || (mtheight & 15))
960 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
962 // LordHavoc: force all names to lowercase
963 for (j = 0;name[j];j++)
964 if (name[j] >= 'A' && name[j] <= 'Z')
965 name[j] += 'a' - 'A';
967 tx = loadmodel->brushq1.textures + i;
968 strcpy(tx->name, name);
970 tx->height = mtheight;
974 sprintf(tx->name, "unnamed%i", i);
975 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
978 // LordHavoc: HL sky textures are entirely different than quake
979 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
981 if (loadmodel->isworldmodel)
983 data = loadimagepixels(tx->name, false, 0, 0);
986 if (image_width == 256 && image_height == 128)
994 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
996 R_InitSky(mtdata, 1);
999 else if (mtdata != NULL)
1000 R_InitSky(mtdata, 1);
1005 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
1007 // did not find external texture, load it from the bsp or wad3
1008 if (loadmodel->brush.ishlbsp)
1010 // internal texture overrides wad
1011 qbyte *pixels, *freepixels, *fogpixels;
1012 pixels = freepixels = NULL;
1014 pixels = W_ConvertWAD3Texture(dmiptex);
1016 pixels = freepixels = W_GetTexture(tx->name);
1019 tx->width = image_width;
1020 tx->height = image_height;
1021 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);
1022 if (Image_CheckAlpha(pixels, image_width * image_height, true))
1024 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
1025 for (j = 0;j < image_width * image_height * 4;j += 4)
1027 fogpixels[j + 0] = 255;
1028 fogpixels[j + 1] = 255;
1029 fogpixels[j + 2] = 255;
1030 fogpixels[j + 3] = pixels[j + 3];
1032 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
1033 Mem_Free(fogpixels);
1037 Mem_Free(freepixels);
1039 else if (mtdata) // texture included
1040 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
1043 if (tx->skin.base == NULL)
1048 tx->skin.base = r_notexture;
1051 if (tx->name[0] == '*')
1053 // turb does not block movement
1054 tx->flags &= ~SURF_SOLIDCLIP;
1055 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
1056 // LordHavoc: some turbulent textures should be fullbright and solid
1057 if (!strncmp(tx->name,"*lava",5)
1058 || !strncmp(tx->name,"*teleport",9)
1059 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
1060 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
1062 tx->flags |= SURF_WATERALPHA;
1063 tx->shader = &Cshader_water;
1065 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
1067 tx->flags |= SURF_DRAWSKY;
1068 tx->shader = &Cshader_sky;
1072 tx->flags |= SURF_LIGHTMAP;
1074 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
1075 tx->shader = &Cshader_wall_lightmap;
1078 // start out with no animation
1079 tx->currentframe = tx;
1082 // sequence the animations
1083 for (i = 0;i < m->nummiptex;i++)
1085 tx = loadmodel->brushq1.textures + i;
1086 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
1088 if (tx->anim_total[0] || tx->anim_total[1])
1089 continue; // already sequenced
1091 // find the number of frames in the animation
1092 memset(anims, 0, sizeof(anims));
1093 memset(altanims, 0, sizeof(altanims));
1095 for (j = i;j < m->nummiptex;j++)
1097 tx2 = loadmodel->brushq1.textures + j;
1098 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
1102 if (num >= '0' && num <= '9')
1103 anims[num - '0'] = tx2;
1104 else if (num >= 'a' && num <= 'j')
1105 altanims[num - 'a'] = tx2;
1107 Con_Printf("Bad animating texture %s\n", tx->name);
1111 for (j = 0;j < 10;j++)
1118 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
1121 for (j = 0;j < max;j++)
1125 Con_Printf("Missing frame %i of %s\n", j, tx->name);
1129 for (j = 0;j < altmax;j++)
1133 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
1142 // if there is no alternate animation, duplicate the primary
1143 // animation into the alternate
1145 for (k = 0;k < 10;k++)
1146 altanims[k] = anims[k];
1149 // link together the primary animation
1150 for (j = 0;j < max;j++)
1153 tx2->animated = true;
1154 tx2->anim_total[0] = max;
1155 tx2->anim_total[1] = altmax;
1156 for (k = 0;k < 10;k++)
1158 tx2->anim_frames[0][k] = anims[k];
1159 tx2->anim_frames[1][k] = altanims[k];
1163 // if there really is an alternate anim...
1164 if (anims[0] != altanims[0])
1166 // link together the alternate animation
1167 for (j = 0;j < altmax;j++)
1170 tx2->animated = true;
1171 // the primary/alternate are reversed here
1172 tx2->anim_total[0] = altmax;
1173 tx2->anim_total[1] = max;
1174 for (k = 0;k < 10;k++)
1176 tx2->anim_frames[0][k] = altanims[k];
1177 tx2->anim_frames[1][k] = anims[k];
1184 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1187 qbyte *in, *out, *data, d;
1188 char litfilename[1024];
1189 loadmodel->brushq1.lightdata = NULL;
1190 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1192 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1193 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1195 else // LordHavoc: bsp version 29 (normal white lighting)
1197 // LordHavoc: hope is not lost yet, check for a .lit file to load
1198 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1199 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1200 strlcat (litfilename, ".lit", sizeof (litfilename));
1201 data = (qbyte*) FS_LoadFile(litfilename, false);
1204 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1206 i = LittleLong(((int *)data)[1]);
1209 Con_DPrintf("loaded %s\n", litfilename);
1210 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1211 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1217 Con_Printf("Unknown .lit file version (%d)\n", i);
1223 if (fs_filesize == 8)
1224 Con_Printf("Empty .lit file, ignoring\n");
1226 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1230 // LordHavoc: oh well, expand the white lighting data
1233 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1234 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1235 out = loadmodel->brushq1.lightdata;
1236 memcpy(in, mod_base + l->fileofs, l->filelen);
1237 for (i = 0;i < l->filelen;i++)
1247 static void Mod_Q1BSP_LoadLightList(void)
1249 int a, n, numlights;
1250 char lightsfilename[1024], *s, *t, *lightsstring;
1253 strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1254 FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1255 strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1256 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1262 while (*s && *s != '\n')
1266 Mem_Free(lightsstring);
1267 Host_Error("lights file must end with a newline\n");
1272 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1275 while (*s && n < numlights)
1278 while (*s && *s != '\n')
1282 Mem_Free(lightsstring);
1283 Host_Error("misparsed lights file!\n");
1285 e = loadmodel->brushq1.lights + n;
1287 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);
1291 Mem_Free(lightsstring);
1292 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);
1299 Mem_Free(lightsstring);
1300 Host_Error("misparsed lights file!\n");
1302 loadmodel->brushq1.numlights = numlights;
1303 Mem_Free(lightsstring);
1307 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1309 loadmodel->brushq1.num_compressedpvs = 0;
1310 loadmodel->brushq1.data_compressedpvs = NULL;
1313 loadmodel->brushq1.num_compressedpvs = l->filelen;
1314 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1315 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1318 // used only for HalfLife maps
1319 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1321 char key[128], value[4096];
1326 if (!COM_ParseToken(&data, false))
1328 if (com_token[0] != '{')
1332 if (!COM_ParseToken(&data, false))
1334 if (com_token[0] == '}')
1335 break; // end of worldspawn
1336 if (com_token[0] == '_')
1337 strcpy(key, com_token + 1);
1339 strcpy(key, com_token);
1340 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1341 key[strlen(key)-1] = 0;
1342 if (!COM_ParseToken(&data, false))
1344 strcpy(value, com_token);
1345 if (!strcmp("wad", key)) // for HalfLife maps
1347 if (loadmodel->brush.ishlbsp)
1350 for (i = 0;i < 4096;i++)
1351 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1357 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1358 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1360 else if (value[i] == ';' || value[i] == 0)
1364 strcpy(wadname, "textures/");
1365 strcat(wadname, &value[j]);
1366 W_LoadTextureWadFile(wadname, false);
1378 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1380 loadmodel->brush.entities = NULL;
1383 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1384 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1385 if (loadmodel->brush.ishlbsp)
1386 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1390 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1396 in = (void *)(mod_base + l->fileofs);
1397 if (l->filelen % sizeof(*in))
1398 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1399 count = l->filelen / sizeof(*in);
1400 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1402 loadmodel->brushq1.vertexes = out;
1403 loadmodel->brushq1.numvertexes = count;
1405 for ( i=0 ; i<count ; i++, in++, out++)
1407 out->position[0] = LittleFloat(in->point[0]);
1408 out->position[1] = LittleFloat(in->point[1]);
1409 out->position[2] = LittleFloat(in->point[2]);
1413 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1419 in = (void *)(mod_base + l->fileofs);
1420 if (l->filelen % sizeof(*in))
1421 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1422 count = l->filelen / sizeof(*in);
1423 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1425 loadmodel->brushq1.submodels = out;
1426 loadmodel->brush.numsubmodels = count;
1428 for ( i=0 ; i<count ; i++, in++, out++)
1430 for (j=0 ; j<3 ; j++)
1432 // spread the mins / maxs by a pixel
1433 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1434 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1435 out->origin[j] = LittleFloat(in->origin[j]);
1437 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1438 out->headnode[j] = LittleLong(in->headnode[j]);
1439 out->visleafs = LittleLong(in->visleafs);
1440 out->firstface = LittleLong(in->firstface);
1441 out->numfaces = LittleLong(in->numfaces);
1445 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1451 in = (void *)(mod_base + l->fileofs);
1452 if (l->filelen % sizeof(*in))
1453 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1454 count = l->filelen / sizeof(*in);
1455 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1457 loadmodel->brushq1.edges = out;
1458 loadmodel->brushq1.numedges = count;
1460 for ( i=0 ; i<count ; i++, in++, out++)
1462 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1463 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1467 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1471 int i, j, k, count, miptex;
1473 in = (void *)(mod_base + l->fileofs);
1474 if (l->filelen % sizeof(*in))
1475 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1476 count = l->filelen / sizeof(*in);
1477 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1479 loadmodel->brushq1.texinfo = out;
1480 loadmodel->brushq1.numtexinfo = count;
1482 for (i = 0;i < count;i++, in++, out++)
1484 for (k = 0;k < 2;k++)
1485 for (j = 0;j < 4;j++)
1486 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1488 miptex = LittleLong(in->miptex);
1489 out->flags = LittleLong(in->flags);
1491 out->texture = NULL;
1492 if (loadmodel->brushq1.textures)
1494 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1495 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1497 out->texture = loadmodel->brushq1.textures + miptex;
1499 if (out->flags & TEX_SPECIAL)
1501 // if texture chosen is NULL or the shader needs a lightmap,
1502 // force to notexture water shader
1503 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1504 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1508 // if texture chosen is NULL, force to notexture
1509 if (out->texture == NULL)
1510 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1516 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1521 mins[0] = mins[1] = mins[2] = 9999;
1522 maxs[0] = maxs[1] = maxs[2] = -9999;
1524 for (i = 0;i < numverts;i++)
1526 for (j = 0;j < 3;j++, v++)
1536 #define MAX_SUBDIVPOLYTRIANGLES 4096
1537 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1539 static int subdivpolyverts, subdivpolytriangles;
1540 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1541 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1543 static int subdivpolylookupvert(vec3_t v)
1546 for (i = 0;i < subdivpolyverts;i++)
1547 if (subdivpolyvert[i][0] == v[0]
1548 && subdivpolyvert[i][1] == v[1]
1549 && subdivpolyvert[i][2] == v[2])
1551 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1552 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1553 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1554 return subdivpolyverts++;
1557 static void SubdividePolygon(int numverts, float *verts)
1559 int i, i1, i2, i3, f, b, c, p;
1560 vec3_t mins, maxs, front[256], back[256];
1561 float m, *pv, *cv, dist[256], frac;
1564 Host_Error("SubdividePolygon: ran out of verts in buffer");
1566 BoundPoly(numverts, verts, mins, maxs);
1568 for (i = 0;i < 3;i++)
1570 m = (mins[i] + maxs[i]) * 0.5;
1571 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1572 if (maxs[i] - m < 8)
1574 if (m - mins[i] < 8)
1578 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1579 dist[c] = cv[i] - m;
1582 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1586 VectorCopy(pv, front[f]);
1591 VectorCopy(pv, back[b]);
1594 if (dist[p] == 0 || dist[c] == 0)
1596 if ((dist[p] > 0) != (dist[c] > 0) )
1599 frac = dist[p] / (dist[p] - dist[c]);
1600 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1601 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1602 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1608 SubdividePolygon(f, front[0]);
1609 SubdividePolygon(b, back[0]);
1613 i1 = subdivpolylookupvert(verts);
1614 i2 = subdivpolylookupvert(verts + 3);
1615 for (i = 2;i < numverts;i++)
1617 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1619 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1623 i3 = subdivpolylookupvert(verts + i * 3);
1624 subdivpolyindex[subdivpolytriangles][0] = i1;
1625 subdivpolyindex[subdivpolytriangles][1] = i2;
1626 subdivpolyindex[subdivpolytriangles][2] = i3;
1628 subdivpolytriangles++;
1632 //Breaks a polygon up along axial 64 unit
1633 //boundaries so that turbulent and sky warps
1634 //can be done reasonably.
1635 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1641 subdivpolytriangles = 0;
1642 subdivpolyverts = 0;
1643 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1644 if (subdivpolytriangles < 1)
1645 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1647 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1648 mesh->num_vertices = subdivpolyverts;
1649 mesh->num_triangles = subdivpolytriangles;
1650 mesh->vertex = (surfvertex_t *)(mesh + 1);
1651 mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1652 memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1654 for (i = 0;i < mesh->num_triangles;i++)
1655 for (j = 0;j < 3;j++)
1656 mesh->index[i*3+j] = subdivpolyindex[i][j];
1658 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1660 VectorCopy(subdivpolyvert[i], v->v);
1661 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1662 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1667 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1670 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1671 mesh->num_vertices = numverts;
1672 mesh->num_triangles = numtriangles;
1673 mesh->data_vertex3f = (float *)(mesh + 1);
1674 mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1675 mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1676 mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1677 mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1678 mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1679 mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1680 mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1681 mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1682 mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1686 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1689 float *vec, *vert, mins[3], maxs[3], val, *v;
1692 // convert edges back to a normal polygon
1693 surf->poly_numverts = numedges;
1694 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1695 for (i = 0;i < numedges;i++)
1697 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1699 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1701 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1702 VectorCopy(vec, vert);
1706 // calculate polygon bounding box and center
1707 vert = surf->poly_verts;
1708 VectorCopy(vert, mins);
1709 VectorCopy(vert, maxs);
1711 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1713 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1714 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1715 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1717 VectorCopy(mins, surf->poly_mins);
1718 VectorCopy(maxs, surf->poly_maxs);
1719 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1720 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1721 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1723 // generate surface extents information
1724 tex = surf->texinfo;
1725 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1726 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1727 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1729 for (j = 0;j < 2;j++)
1731 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1738 for (i = 0;i < 2;i++)
1740 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1741 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1745 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1749 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1753 in = (void *)(mod_base + l->fileofs);
1754 if (l->filelen % sizeof(*in))
1755 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1756 count = l->filelen / sizeof(*in);
1757 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1759 loadmodel->brushq1.numsurfaces = count;
1760 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1761 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1762 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1764 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++)
1766 surf->number = surfnum;
1767 // FIXME: validate edges, texinfo, etc?
1768 firstedge = LittleLong(in->firstedge);
1769 numedges = LittleShort(in->numedges);
1770 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)
1771 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1772 i = LittleShort(in->texinfo);
1773 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1774 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1775 surf->texinfo = loadmodel->brushq1.texinfo + i;
1776 surf->flags = surf->texinfo->texture->flags;
1778 planenum = LittleShort(in->planenum);
1779 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1780 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1782 if (LittleShort(in->side))
1783 surf->flags |= SURF_PLANEBACK;
1785 surf->plane = loadmodel->brushq1.planes + planenum;
1787 // clear lightmap (filled in later)
1788 surf->lightmaptexture = NULL;
1790 // force lightmap upload on first time seeing the surface
1791 surf->cached_dlight = true;
1793 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1795 ssize = (surf->extents[0] >> 4) + 1;
1796 tsize = (surf->extents[1] >> 4) + 1;
1799 for (i = 0;i < MAXLIGHTMAPS;i++)
1800 surf->styles[i] = in->styles[i];
1801 i = LittleLong(in->lightofs);
1803 surf->samples = NULL;
1804 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1805 surf->samples = loadmodel->brushq1.lightdata + i;
1806 else // LordHavoc: white lighting (bsp version 29)
1807 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1809 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1811 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1812 Host_Error("Bad surface extents");
1813 // stainmap for permanent marks on walls
1814 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1816 memset(surf->stainsamples, 255, ssize * tsize * 3);
1820 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1822 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++)
1825 mesh->num_vertices = surf->poly_numverts;
1826 mesh->num_triangles = surf->poly_numverts - 2;
1827 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1828 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1829 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1830 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1831 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1832 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1833 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1834 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1835 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1836 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1838 surf->lightmaptexturestride = 0;
1839 surf->lightmaptexture = NULL;
1841 for (i = 0;i < mesh->num_vertices;i++)
1843 mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1844 mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1845 mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1846 s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1847 t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1848 mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1849 mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1850 mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1851 mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1852 mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1853 mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1854 mesh->data_lightmapoffsets[i] = 0;
1857 for (i = 0;i < mesh->num_triangles;i++)
1859 mesh->data_element3i[i * 3 + 0] = 0;
1860 mesh->data_element3i[i * 3 + 1] = i + 1;
1861 mesh->data_element3i[i * 3 + 2] = i + 2;
1864 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1865 Mod_BuildTextureVectorsAndNormals(mesh->num_vertices, mesh->num_triangles, mesh->data_vertex3f, mesh->data_texcoordtexture2f, mesh->data_element3i, mesh->data_svector3f, mesh->data_tvector3f, mesh->data_normal3f);
1867 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1869 int i, iu, iv, smax, tmax;
1870 float u, v, ubase, vbase, uscale, vscale;
1872 smax = surf->extents[0] >> 4;
1873 tmax = surf->extents[1] >> 4;
1875 surf->flags |= SURF_LIGHTMAP;
1876 if (r_miplightmaps.integer)
1878 surf->lightmaptexturestride = smax+1;
1879 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);
1883 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1884 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);
1886 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1887 uscale = (uscale - ubase) / (smax + 1);
1888 vscale = (vscale - vbase) / (tmax + 1);
1890 for (i = 0;i < mesh->num_vertices;i++)
1892 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1893 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1894 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1895 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1896 // LordHavoc: calc lightmap data offset for vertex lighting to use
1899 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1905 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1907 node->parent = parent;
1908 if (node->contents < 0)
1910 Mod_Q1BSP_SetParent(node->children[0], node);
1911 Mod_Q1BSP_SetParent(node->children[1], node);
1914 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1920 in = (void *)(mod_base + l->fileofs);
1921 if (l->filelen % sizeof(*in))
1922 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1923 count = l->filelen / sizeof(*in);
1924 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1926 loadmodel->brushq1.nodes = out;
1927 loadmodel->brushq1.numnodes = count;
1929 for ( i=0 ; i<count ; i++, in++, out++)
1931 for (j=0 ; j<3 ; j++)
1933 out->mins[j] = LittleShort(in->mins[j]);
1934 out->maxs[j] = LittleShort(in->maxs[j]);
1937 p = LittleLong(in->planenum);
1938 out->plane = loadmodel->brushq1.planes + p;
1940 out->firstsurface = LittleShort(in->firstface);
1941 out->numsurfaces = LittleShort(in->numfaces);
1943 for (j=0 ; j<2 ; j++)
1945 p = LittleShort(in->children[j]);
1947 out->children[j] = loadmodel->brushq1.nodes + p;
1949 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1953 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1956 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1960 int i, j, count, p, pvschainbytes;
1963 in = (void *)(mod_base + l->fileofs);
1964 if (l->filelen % sizeof(*in))
1965 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1966 count = l->filelen / sizeof(*in);
1967 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1969 loadmodel->brushq1.leafs = out;
1970 loadmodel->brushq1.numleafs = count;
1971 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1972 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1974 for ( i=0 ; i<count ; i++, in++, out++)
1976 for (j=0 ; j<3 ; j++)
1978 out->mins[j] = LittleShort(in->mins[j]);
1979 out->maxs[j] = LittleShort(in->maxs[j]);
1982 // FIXME: this function could really benefit from some error checking
1984 out->contents = LittleLong(in->contents);
1986 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1987 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1988 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
1990 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid marksurface range %i:%i outside range %i:%i\n", out->firstmarksurface, out->firstmarksurface + out->nummarksurfaces, 0, loadmodel->brushq1.nummarksurfaces);
1991 out->firstmarksurface = NULL;
1992 out->nummarksurfaces = 0;
1996 memset(out->pvsdata, 0xFF, pvschainbytes);
1997 pvs += pvschainbytes;
1999 p = LittleLong(in->visofs);
2002 if (p >= loadmodel->brushq1.num_compressedpvs)
2003 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
2005 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
2008 for (j = 0;j < 4;j++)
2009 out->ambient_sound_level[j] = in->ambient_level[j];
2011 // FIXME: Insert caustics here
2015 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
2017 dclipnode_t *in, *out;
2021 in = (void *)(mod_base + l->fileofs);
2022 if (l->filelen % sizeof(*in))
2023 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2024 count = l->filelen / sizeof(*in);
2025 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2027 loadmodel->brushq1.clipnodes = out;
2028 loadmodel->brushq1.numclipnodes = count;
2030 if (loadmodel->brush.ishlbsp)
2032 hull = &loadmodel->brushq1.hulls[1];
2033 hull->clipnodes = out;
2034 hull->firstclipnode = 0;
2035 hull->lastclipnode = count-1;
2036 hull->planes = loadmodel->brushq1.planes;
2037 hull->clip_mins[0] = -16;
2038 hull->clip_mins[1] = -16;
2039 hull->clip_mins[2] = -36;
2040 hull->clip_maxs[0] = 16;
2041 hull->clip_maxs[1] = 16;
2042 hull->clip_maxs[2] = 36;
2043 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2045 hull = &loadmodel->brushq1.hulls[2];
2046 hull->clipnodes = out;
2047 hull->firstclipnode = 0;
2048 hull->lastclipnode = count-1;
2049 hull->planes = loadmodel->brushq1.planes;
2050 hull->clip_mins[0] = -32;
2051 hull->clip_mins[1] = -32;
2052 hull->clip_mins[2] = -32;
2053 hull->clip_maxs[0] = 32;
2054 hull->clip_maxs[1] = 32;
2055 hull->clip_maxs[2] = 32;
2056 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2058 hull = &loadmodel->brushq1.hulls[3];
2059 hull->clipnodes = out;
2060 hull->firstclipnode = 0;
2061 hull->lastclipnode = count-1;
2062 hull->planes = loadmodel->brushq1.planes;
2063 hull->clip_mins[0] = -16;
2064 hull->clip_mins[1] = -16;
2065 hull->clip_mins[2] = -18;
2066 hull->clip_maxs[0] = 16;
2067 hull->clip_maxs[1] = 16;
2068 hull->clip_maxs[2] = 18;
2069 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2073 hull = &loadmodel->brushq1.hulls[1];
2074 hull->clipnodes = out;
2075 hull->firstclipnode = 0;
2076 hull->lastclipnode = count-1;
2077 hull->planes = loadmodel->brushq1.planes;
2078 hull->clip_mins[0] = -16;
2079 hull->clip_mins[1] = -16;
2080 hull->clip_mins[2] = -24;
2081 hull->clip_maxs[0] = 16;
2082 hull->clip_maxs[1] = 16;
2083 hull->clip_maxs[2] = 32;
2084 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2086 hull = &loadmodel->brushq1.hulls[2];
2087 hull->clipnodes = out;
2088 hull->firstclipnode = 0;
2089 hull->lastclipnode = count-1;
2090 hull->planes = loadmodel->brushq1.planes;
2091 hull->clip_mins[0] = -32;
2092 hull->clip_mins[1] = -32;
2093 hull->clip_mins[2] = -24;
2094 hull->clip_maxs[0] = 32;
2095 hull->clip_maxs[1] = 32;
2096 hull->clip_maxs[2] = 64;
2097 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2100 for (i=0 ; i<count ; i++, out++, in++)
2102 out->planenum = LittleLong(in->planenum);
2103 out->children[0] = LittleShort(in->children[0]);
2104 out->children[1] = LittleShort(in->children[1]);
2105 if (out->children[0] >= count || out->children[1] >= count)
2106 Host_Error("Corrupt clipping hull(out of range child)\n");
2110 //Duplicate the drawing hull structure as a clipping hull
2111 static void Mod_Q1BSP_MakeHull0(void)
2118 hull = &loadmodel->brushq1.hulls[0];
2120 in = loadmodel->brushq1.nodes;
2121 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
2123 hull->clipnodes = out;
2124 hull->firstclipnode = 0;
2125 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
2126 hull->planes = loadmodel->brushq1.planes;
2128 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
2130 out->planenum = in->plane - loadmodel->brushq1.planes;
2131 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
2132 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
2136 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
2141 in = (void *)(mod_base + l->fileofs);
2142 if (l->filelen % sizeof(*in))
2143 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2144 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2145 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2147 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2149 j = (unsigned) LittleShort(in[i]);
2150 if (j >= loadmodel->brushq1.numsurfaces)
2151 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2152 loadmodel->brushq1.marksurfaces[i] = j;
2156 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2161 in = (void *)(mod_base + l->fileofs);
2162 if (l->filelen % sizeof(*in))
2163 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2164 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2165 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2167 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2168 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2172 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2178 in = (void *)(mod_base + l->fileofs);
2179 if (l->filelen % sizeof(*in))
2180 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2182 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2183 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2185 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2187 out->normal[0] = LittleFloat(in->normal[0]);
2188 out->normal[1] = LittleFloat(in->normal[1]);
2189 out->normal[2] = LittleFloat(in->normal[2]);
2190 out->dist = LittleFloat(in->dist);
2196 typedef struct portal_s
2199 mnode_t *nodes[2]; // [0] = front side of plane
2200 struct portal_s *next[2];
2202 struct portal_s *chain; // all portals are linked into a list
2206 static portal_t *portalchain;
2213 static portal_t *AllocPortal(void)
2216 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2217 p->chain = portalchain;
2222 static void FreePortal(portal_t *p)
2227 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2229 // calculate children first
2230 if (node->children[0]->contents >= 0)
2231 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2232 if (node->children[1]->contents >= 0)
2233 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2235 // make combined bounding box from children
2236 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2237 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2238 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2239 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2240 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2241 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2244 static void Mod_Q1BSP_FinalizePortals(void)
2246 int i, j, numportals, numpoints;
2247 portal_t *p, *pnext;
2250 mleaf_t *leaf, *endleaf;
2253 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2254 leaf = loadmodel->brushq1.leafs;
2255 endleaf = leaf + loadmodel->brushq1.numleafs;
2256 for (;leaf < endleaf;leaf++)
2258 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2259 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2266 for (i = 0;i < 2;i++)
2268 leaf = (mleaf_t *)p->nodes[i];
2270 for (j = 0;j < w->numpoints;j++)
2272 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2273 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2274 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2275 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2276 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2277 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2284 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2286 // tally up portal and point counts
2292 // note: this check must match the one below or it will usually corrupt memory
2293 // 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
2294 if (p->winding && p->nodes[0] != p->nodes[1]
2295 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2296 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2299 numpoints += p->winding->numpoints * 2;
2303 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2304 loadmodel->brushq1.numportals = numportals;
2305 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2306 loadmodel->brushq1.numportalpoints = numpoints;
2307 // clear all leaf portal chains
2308 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2309 loadmodel->brushq1.leafs[i].portals = NULL;
2310 // process all portals in the global portal chain, while freeing them
2311 portal = loadmodel->brushq1.portals;
2312 point = loadmodel->brushq1.portalpoints;
2321 // note: this check must match the one above or it will usually corrupt memory
2322 // 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
2323 if (p->nodes[0] != p->nodes[1]
2324 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2325 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2327 // first make the back to front portal(forward portal)
2328 portal->points = point;
2329 portal->numpoints = p->winding->numpoints;
2330 portal->plane.dist = p->plane.dist;
2331 VectorCopy(p->plane.normal, portal->plane.normal);
2332 portal->here = (mleaf_t *)p->nodes[1];
2333 portal->past = (mleaf_t *)p->nodes[0];
2335 for (j = 0;j < portal->numpoints;j++)
2337 VectorCopy(p->winding->points[j], point->position);
2340 PlaneClassify(&portal->plane);
2342 // link into leaf's portal chain
2343 portal->next = portal->here->portals;
2344 portal->here->portals = portal;
2346 // advance to next portal
2349 // then make the front to back portal(backward portal)
2350 portal->points = point;
2351 portal->numpoints = p->winding->numpoints;
2352 portal->plane.dist = -p->plane.dist;
2353 VectorNegate(p->plane.normal, portal->plane.normal);
2354 portal->here = (mleaf_t *)p->nodes[0];
2355 portal->past = (mleaf_t *)p->nodes[1];
2357 for (j = portal->numpoints - 1;j >= 0;j--)
2359 VectorCopy(p->winding->points[j], point->position);
2362 PlaneClassify(&portal->plane);
2364 // link into leaf's portal chain
2365 portal->next = portal->here->portals;
2366 portal->here->portals = portal;
2368 // advance to next portal
2371 Winding_Free(p->winding);
2383 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2386 Host_Error("AddPortalToNodes: NULL front node");
2388 Host_Error("AddPortalToNodes: NULL back node");
2389 if (p->nodes[0] || p->nodes[1])
2390 Host_Error("AddPortalToNodes: already included");
2391 // 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
2393 p->nodes[0] = front;
2394 p->next[0] = (portal_t *)front->portals;
2395 front->portals = (mportal_t *)p;
2398 p->next[1] = (portal_t *)back->portals;
2399 back->portals = (mportal_t *)p;
2404 RemovePortalFromNode
2407 static void RemovePortalFromNodes(portal_t *portal)
2411 void **portalpointer;
2413 for (i = 0;i < 2;i++)
2415 node = portal->nodes[i];
2417 portalpointer = (void **) &node->portals;
2422 Host_Error("RemovePortalFromNodes: portal not in leaf");
2426 if (portal->nodes[0] == node)
2428 *portalpointer = portal->next[0];
2429 portal->nodes[0] = NULL;
2431 else if (portal->nodes[1] == node)
2433 *portalpointer = portal->next[1];
2434 portal->nodes[1] = NULL;
2437 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2441 if (t->nodes[0] == node)
2442 portalpointer = (void **) &t->next[0];
2443 else if (t->nodes[1] == node)
2444 portalpointer = (void **) &t->next[1];
2446 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2451 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2454 mnode_t *front, *back, *other_node;
2455 mplane_t clipplane, *plane;
2456 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2457 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2459 // if a leaf, we're done
2463 plane = node->plane;
2465 front = node->children[0];
2466 back = node->children[1];
2468 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2470 // create the new portal by generating a polygon for the node plane,
2471 // and clipping it by all of the other portals(which came from nodes above this one)
2472 nodeportal = AllocPortal();
2473 nodeportal->plane = *plane;
2475 nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2476 side = 0; // shut up compiler warning
2477 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2479 clipplane = portal->plane;
2480 if (portal->nodes[0] == portal->nodes[1])
2481 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2482 if (portal->nodes[0] == node)
2484 else if (portal->nodes[1] == node)
2486 clipplane.dist = -clipplane.dist;
2487 VectorNegate(clipplane.normal, clipplane.normal);
2491 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2493 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2494 if (!nodeportalwinding)
2496 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2501 if (nodeportalwinding)
2503 // if the plane was not clipped on all sides, there was an error
2504 nodeportal->winding = nodeportalwinding;
2505 AddPortalToNodes(nodeportal, front, back);
2508 // split the portals of this node along this node's plane and assign them to the children of this node
2509 // (migrating the portals downward through the tree)
2510 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2512 if (portal->nodes[0] == portal->nodes[1])
2513 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2514 if (portal->nodes[0] == node)
2516 else if (portal->nodes[1] == node)
2519 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2520 nextportal = portal->next[side];
2522 other_node = portal->nodes[!side];
2523 RemovePortalFromNodes(portal);
2525 // cut the portal into two portals, one on each side of the node plane
2526 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2531 AddPortalToNodes(portal, back, other_node);
2533 AddPortalToNodes(portal, other_node, back);
2539 AddPortalToNodes(portal, front, other_node);
2541 AddPortalToNodes(portal, other_node, front);
2545 // the winding is split
2546 splitportal = AllocPortal();
2547 temp = splitportal->chain;
2548 *splitportal = *portal;
2549 splitportal->chain = temp;
2550 splitportal->winding = backwinding;
2551 Winding_Free(portal->winding);
2552 portal->winding = frontwinding;
2556 AddPortalToNodes(portal, front, other_node);
2557 AddPortalToNodes(splitportal, back, other_node);
2561 AddPortalToNodes(portal, other_node, front);
2562 AddPortalToNodes(splitportal, other_node, back);
2566 Mod_Q1BSP_RecursiveNodePortals(front);
2567 Mod_Q1BSP_RecursiveNodePortals(back);
2570 static void Mod_Q1BSP_MakePortals(void)
2573 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2574 Mod_Q1BSP_FinalizePortals();
2577 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2580 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2581 msurface_t *surf, *s;
2582 float *v0, *v1, *v2, *v3;
2583 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2584 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2585 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2587 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)
2589 if (surf->neighborsurfaces[vertnum])
2591 surf->neighborsurfaces[vertnum] = NULL;
2592 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2594 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2595 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2596 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2599 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2600 if (s->neighborsurfaces[vnum] == surf)
2602 if (vnum < s->poly_numverts)
2604 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)
2606 if (s->neighborsurfaces[vnum] == NULL
2607 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2608 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2610 surf->neighborsurfaces[vertnum] = s;
2611 s->neighborsurfaces[vnum] = surf;
2615 if (vnum < s->poly_numverts)
2623 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2625 int i, j, stylecounts[256], totalcount, remapstyles[256];
2627 memset(stylecounts, 0, sizeof(stylecounts));
2628 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2630 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2631 for (j = 0;j < MAXLIGHTMAPS;j++)
2632 stylecounts[surf->styles[j]]++;
2635 model->brushq1.light_styles = 0;
2636 for (i = 0;i < 255;i++)
2640 remapstyles[i] = model->brushq1.light_styles++;
2641 totalcount += stylecounts[i] + 1;
2646 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2647 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2648 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2649 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2650 model->brushq1.light_styles = 0;
2651 for (i = 0;i < 255;i++)
2653 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2655 for (i = 0;i < model->brushq1.light_styles;i++)
2657 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2658 j += stylecounts[model->brushq1.light_style[i]] + 1;
2660 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2662 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2663 for (j = 0;j < MAXLIGHTMAPS;j++)
2664 if (surf->styles[j] != 255)
2665 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2668 for (i = 0;i < model->brushq1.light_styles;i++)
2670 *model->brushq1.light_styleupdatechains[i] = NULL;
2671 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2672 j += stylecounts[model->brushq1.light_style[i]] + 1;
2676 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2679 for (i = 0;i < model->brushq1.numtextures;i++)
2680 model->brushq1.pvstexturechainslength[i] = 0;
2681 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2683 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2685 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2686 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2689 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2691 if (model->brushq1.pvstexturechainslength[i])
2693 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2694 j += model->brushq1.pvstexturechainslength[i] + 1;
2697 model->brushq1.pvstexturechains[i] = NULL;
2699 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2700 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2701 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2702 for (i = 0;i < model->brushq1.numtextures;i++)
2704 if (model->brushq1.pvstexturechainslength[i])
2706 *model->brushq1.pvstexturechains[i] = NULL;
2707 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2712 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2717 while (node->contents >= 0)
2719 d = PlaneDiff(org, node->plane);
2721 node = node->children[0];
2722 else if (d < -radius)
2723 node = node->children[1];
2726 // go down both sides
2727 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2728 node = node->children[1];
2732 // if this is a leaf, accumulate the pvs bits
2733 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2734 for (i = 0;i < pvsbytes;i++)
2735 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2738 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2739 //of the given point.
2740 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2742 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2743 bytes = min(bytes, pvsbufferlength);
2744 memset(pvsbuffer, 0, bytes);
2745 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2749 //Returns PVS data for a given point
2750 //(note: always returns valid data, never NULL)
2751 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2754 Mod_CheckLoaded(model);
2755 // LordHavoc: modified to start at first clip node,
2756 // in other words: first node of the (sub)model
2757 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2758 while (node->contents == 0)
2759 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2760 return ((mleaf_t *)node)->pvsdata;
2763 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2768 VectorSubtract(inmaxs, inmins, size);
2769 if (cmodel->brush.ishlbsp)
2772 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2773 else if (size[0] <= 32)
2775 if (size[2] < 54) // pick the nearest of 36 or 72
2776 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2778 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2781 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2786 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2787 else if (size[0] <= 32)
2788 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2790 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2792 VectorCopy(inmins, outmins);
2793 VectorAdd(inmins, hull->clip_size, outmaxs);
2796 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2797 extern void R_Model_Brush_Draw(entity_render_t *ent);
2798 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2799 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);
2800 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2805 mempool_t *mainmempool;
2807 model_t *originalloadmodel;
2808 float dist, modelyawradius, modelradius, *vec;
2811 mod->type = mod_brush;
2813 header = (dheader_t *)buffer;
2815 i = LittleLong(header->version);
2816 if (i != BSPVERSION && i != 30)
2817 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2818 mod->brush.ishlbsp = i == 30;
2820 mod->soundfromcenter = true;
2821 mod->TraceBox = Mod_Q1BSP_TraceBox;
2822 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2823 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2824 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2825 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2826 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2827 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2828 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2829 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2830 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2831 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2832 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2834 if (loadmodel->isworldmodel)
2836 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2837 // until we get a texture for it...
2841 // swap all the lumps
2842 mod_base = (qbyte *)header;
2844 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2845 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2849 // store which lightmap format to use
2850 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2852 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2853 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2854 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2855 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2856 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2857 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2858 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2859 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2860 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2861 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2862 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2863 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2864 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2865 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2866 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2868 if (mod->brushq1.data_compressedpvs)
2869 Mem_Free(mod->brushq1.data_compressedpvs);
2870 mod->brushq1.data_compressedpvs = NULL;
2871 mod->brushq1.num_compressedpvs = 0;
2873 Mod_Q1BSP_MakeHull0();
2874 Mod_Q1BSP_MakePortals();
2876 if (developer.integer)
2877 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);
2879 mod->numframes = 2; // regular and alternate animation
2881 mainmempool = mod->mempool;
2882 loadname = mod->name;
2884 Mod_Q1BSP_LoadLightList();
2885 originalloadmodel = loadmodel;
2888 // set up the submodels(FIXME: this is confusing)
2890 for (i = 0;i < mod->brush.numsubmodels;i++)
2892 bm = &mod->brushq1.submodels[i];
2894 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2895 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2897 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2898 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2901 mod->brushq1.firstmodelsurface = bm->firstface;
2902 mod->brushq1.nummodelsurfaces = bm->numfaces;
2904 // this gets altered below if sky is used
2905 mod->DrawSky = NULL;
2906 mod->Draw = R_Model_Brush_Draw;
2907 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2908 mod->DrawLight = R_Model_Brush_DrawLight;
2909 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2910 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2911 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2912 Mod_Q1BSP_BuildPVSTextureChains(mod);
2913 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2914 if (mod->brushq1.nummodelsurfaces)
2916 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2917 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2918 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2921 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2923 // we only need to have a drawsky function if it is used(usually only on world model)
2924 if (surf->texinfo->texture->shader == &Cshader_sky)
2925 mod->DrawSky = R_Model_Brush_DrawSky;
2926 // LordHavoc: submodels always clip, even if water
2927 if (mod->brush.numsubmodels - 1)
2928 surf->flags |= SURF_SOLIDCLIP;
2929 // calculate bounding shapes
2930 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
2932 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2933 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2934 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2935 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2936 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2937 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2938 dist = vec[0]*vec[0]+vec[1]*vec[1];
2939 if (modelyawradius < dist)
2940 modelyawradius = dist;
2941 dist += vec[2]*vec[2];
2942 if (modelradius < dist)
2946 modelyawradius = sqrt(modelyawradius);
2947 modelradius = sqrt(modelradius);
2948 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2949 mod->yawmins[2] = mod->normalmins[2];
2950 mod->yawmaxs[2] = mod->normalmaxs[2];
2951 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2952 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2953 mod->radius = modelradius;
2954 mod->radius2 = modelradius * modelradius;
2958 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2959 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2961 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2963 mod->brushq1.visleafs = bm->visleafs;
2965 // LordHavoc: only register submodels if it is the world
2966 // (prevents bsp models from replacing world submodels)
2967 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2970 // duplicate the basic information
2971 sprintf(name, "*%i", i+1);
2972 loadmodel = Mod_FindName(name);
2974 strcpy(loadmodel->name, name);
2975 // textures and memory belong to the main model
2976 loadmodel->texturepool = NULL;
2977 loadmodel->mempool = NULL;
2982 loadmodel = originalloadmodel;
2983 //Mod_Q1BSP_ProcessLightList();
2986 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2990 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2997 in = (void *)(mod_base + l->fileofs);
2998 if (l->filelen % sizeof(*in))
2999 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3000 count = l->filelen / sizeof(*in);
3001 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3004 loadmodel->num = count;
3006 for (i = 0;i < count;i++, in++, out++)
3012 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3019 in = (void *)(mod_base + l->fileofs);
3020 if (l->filelen % sizeof(*in))
3021 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3022 count = l->filelen / sizeof(*in);
3023 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3026 loadmodel->num = count;
3028 for (i = 0;i < count;i++, in++, out++)
3034 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3041 in = (void *)(mod_base + l->fileofs);
3042 if (l->filelen % sizeof(*in))
3043 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3044 count = l->filelen / sizeof(*in);
3045 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3048 loadmodel->num = count;
3050 for (i = 0;i < count;i++, in++, out++)
3056 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3063 in = (void *)(mod_base + l->fileofs);
3064 if (l->filelen % sizeof(*in))
3065 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3066 count = l->filelen / sizeof(*in);
3067 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3070 loadmodel->num = count;
3072 for (i = 0;i < count;i++, in++, out++)
3078 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3085 in = (void *)(mod_base + l->fileofs);
3086 if (l->filelen % sizeof(*in))
3087 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3088 count = l->filelen / sizeof(*in);
3089 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3092 loadmodel->num = count;
3094 for (i = 0;i < count;i++, in++, out++)
3100 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3107 in = (void *)(mod_base + l->fileofs);
3108 if (l->filelen % sizeof(*in))
3109 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3110 count = l->filelen / sizeof(*in);
3111 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3114 loadmodel->num = count;
3116 for (i = 0;i < count;i++, in++, out++)
3122 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3129 in = (void *)(mod_base + l->fileofs);
3130 if (l->filelen % sizeof(*in))
3131 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3132 count = l->filelen / sizeof(*in);
3133 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3136 loadmodel->num = count;
3138 for (i = 0;i < count;i++, in++, out++)
3144 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3151 in = (void *)(mod_base + l->fileofs);
3152 if (l->filelen % sizeof(*in))
3153 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3154 count = l->filelen / sizeof(*in);
3155 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3158 loadmodel->num = count;
3160 for (i = 0;i < count;i++, in++, out++)
3166 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3173 in = (void *)(mod_base + l->fileofs);
3174 if (l->filelen % sizeof(*in))
3175 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3176 count = l->filelen / sizeof(*in);
3177 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3180 loadmodel->num = count;
3182 for (i = 0;i < count;i++, in++, out++)
3188 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3195 in = (void *)(mod_base + l->fileofs);
3196 if (l->filelen % sizeof(*in))
3197 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3198 count = l->filelen / sizeof(*in);
3199 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3202 loadmodel->num = count;
3204 for (i = 0;i < count;i++, in++, out++)
3210 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3217 in = (void *)(mod_base + l->fileofs);
3218 if (l->filelen % sizeof(*in))
3219 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3220 count = l->filelen / sizeof(*in);
3221 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3224 loadmodel->num = count;
3226 for (i = 0;i < count;i++, in++, out++)
3232 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3239 in = (void *)(mod_base + l->fileofs);
3240 if (l->filelen % sizeof(*in))
3241 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3242 count = l->filelen / sizeof(*in);
3243 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3246 loadmodel->num = count;
3248 for (i = 0;i < count;i++, in++, out++)
3254 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3261 in = (void *)(mod_base + l->fileofs);
3262 if (l->filelen % sizeof(*in))
3263 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3264 count = l->filelen / sizeof(*in);
3265 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3268 loadmodel->num = count;
3270 for (i = 0;i < count;i++, in++, out++)
3276 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3283 in = (void *)(mod_base + l->fileofs);
3284 if (l->filelen % sizeof(*in))
3285 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3286 count = l->filelen / sizeof(*in);
3287 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3290 loadmodel->num = count;
3292 for (i = 0;i < count;i++, in++, out++)
3298 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3305 in = (void *)(mod_base + l->fileofs);
3306 if (l->filelen % sizeof(*in))
3307 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3308 count = l->filelen / sizeof(*in);
3309 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3312 loadmodel->num = count;
3314 for (i = 0;i < count;i++, in++, out++)
3320 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3327 in = (void *)(mod_base + l->fileofs);
3328 if (l->filelen % sizeof(*in))
3329 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3330 count = l->filelen / sizeof(*in);
3331 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3334 loadmodel->num = count;
3336 for (i = 0;i < count;i++, in++, out++)
3342 static void Mod_Q2BSP_LoadModels(lump_t *l)
3349 in = (void *)(mod_base + l->fileofs);
3350 if (l->filelen % sizeof(*in))
3351 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3352 count = l->filelen / sizeof(*in);
3353 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3356 loadmodel->num = count;
3358 for (i = 0;i < count;i++, in++, out++)
3364 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3367 q2dheader_t *header;
3369 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3371 mod->type = mod_brushq2;
3373 header = (q2dheader_t *)buffer;
3375 i = LittleLong(header->version);
3376 if (i != Q2BSPVERSION)
3377 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3378 mod->brush.ishlbsp = false;
3379 if (loadmodel->isworldmodel)
3381 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3382 // until we get a texture for it...
3386 mod_base = (qbyte *)header;
3388 // swap all the lumps
3389 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3390 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3392 // store which lightmap format to use
3393 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3395 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3396 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3397 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3398 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3399 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3400 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3401 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3402 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3403 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3404 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3405 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3406 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3407 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3408 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3409 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3410 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3411 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3412 // LordHavoc: must go last because this makes the submodels
3413 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3416 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3417 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3419 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3422 char key[128], value[4096];
3424 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3425 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3426 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3429 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3430 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3431 data = loadmodel->brush.entities;
3432 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3433 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3437 if (!COM_ParseToken(&data, false))
3439 if (com_token[0] == '}')
3440 break; // end of worldspawn
3441 if (com_token[0] == '_')
3442 strcpy(key, com_token + 1);
3444 strcpy(key, com_token);
3445 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3446 key[strlen(key)-1] = 0;
3447 if (!COM_ParseToken(&data, false))
3449 strcpy(value, com_token);
3450 if (!strcmp("gridsize", key))
3452 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3453 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3459 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3469 char shadername[Q3PATHLENGTH];
3471 in = (void *)(mod_base + l->fileofs);
3472 if (l->filelen % sizeof(*in))
3473 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3474 count = l->filelen / sizeof(*in);
3475 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3477 loadmodel->brushq3.data_textures = out;
3478 loadmodel->brushq3.num_textures = count;
3480 for (i = 0;i < count;i++, in++, out++)
3483 strlcpy (out->name, in->name, sizeof (out->name));
3484 out->surfaceflags = LittleLong(in->surfaceflags);
3485 out->nativecontents = LittleLong(in->contents);
3486 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3487 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3488 out->surfaceparms = -1;
3491 // do a quick parse of shader files to get surfaceparms
3492 if ((search = FS_Search("scripts/*.shader", true, false)))
3494 for (i = 0;i < search->numfilenames;i++)
3496 if ((f = FS_LoadFile(search->filenames[i], false)))
3499 while (COM_ParseToken(&text, false))
3501 snprintf(shadername, sizeof(shadername), "%s", com_token);
3503 if (COM_ParseToken(&text, false) && !strcmp(com_token, "{"))
3505 while (COM_ParseToken(&text, false))
3507 if (!strcmp(com_token, "}"))
3509 else if (!strcmp(com_token, "{"))
3511 while (COM_ParseToken(&text, false))
3513 if (!strcmp(com_token, "}"))
3517 else if (!strcmp(com_token, "surfaceparm"))
3519 if (COM_ParseToken(&text, true) && strcmp(com_token, "\n"))
3521 if (!strcmp(com_token, "alphashadow"))
3522 flags |= Q3SURFACEPARM_ALPHASHADOW;
3523 else if (!strcmp(com_token, "areaportal"))
3524 flags |= Q3SURFACEPARM_AREAPORTAL;
3525 else if (!strcmp(com_token, "clusterportal"))
3526 flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3527 else if (!strcmp(com_token, "detail"))
3528 flags |= Q3SURFACEPARM_DETAIL;
3529 else if (!strcmp(com_token, "donotenter"))
3530 flags |= Q3SURFACEPARM_DONOTENTER;
3531 else if (!strcmp(com_token, "fog"))
3532 flags |= Q3SURFACEPARM_FOG;
3533 else if (!strcmp(com_token, "lava"))
3534 flags |= Q3SURFACEPARM_LAVA;
3535 else if (!strcmp(com_token, "lightfilter"))
3536 flags |= Q3SURFACEPARM_LIGHTFILTER;
3537 else if (!strcmp(com_token, "metalsteps"))
3538 flags |= Q3SURFACEPARM_METALSTEPS;
3539 else if (!strcmp(com_token, "nodamage"))
3540 flags |= Q3SURFACEPARM_NODAMAGE;
3541 else if (!strcmp(com_token, "nodlight"))
3542 flags |= Q3SURFACEPARM_NODLIGHT;
3543 else if (!strcmp(com_token, "nodraw"))
3544 flags |= Q3SURFACEPARM_NODRAW;
3545 else if (!strcmp(com_token, "nodrop"))
3546 flags |= Q3SURFACEPARM_NODROP;
3547 else if (!strcmp(com_token, "noimpact"))
3548 flags |= Q3SURFACEPARM_NOIMPACT;
3549 else if (!strcmp(com_token, "nolightmap"))
3550 flags |= Q3SURFACEPARM_NOLIGHTMAP;
3551 else if (!strcmp(com_token, "nomarks"))
3552 flags |= Q3SURFACEPARM_NOMARKS;
3553 else if (!strcmp(com_token, "nomipmaps"))
3554 flags |= Q3SURFACEPARM_NOMIPMAPS;
3555 else if (!strcmp(com_token, "nonsolid"))
3556 flags |= Q3SURFACEPARM_NONSOLID;
3557 else if (!strcmp(com_token, "origin"))
3558 flags |= Q3SURFACEPARM_ORIGIN;
3559 else if (!strcmp(com_token, "playerclip"))
3560 flags |= Q3SURFACEPARM_PLAYERCLIP;
3561 else if (!strcmp(com_token, "sky"))
3562 flags |= Q3SURFACEPARM_SKY;
3563 else if (!strcmp(com_token, "slick"))
3564 flags |= Q3SURFACEPARM_SLICK;
3565 else if (!strcmp(com_token, "slime"))
3566 flags |= Q3SURFACEPARM_SLIME;
3567 else if (!strcmp(com_token, "structural"))
3568 flags |= Q3SURFACEPARM_STRUCTURAL;
3569 else if (!strcmp(com_token, "trans"))
3570 flags |= Q3SURFACEPARM_TRANS;
3571 else if (!strcmp(com_token, "water"))
3572 flags |= Q3SURFACEPARM_WATER;
3574 Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
3575 if (!COM_ParseToken(&text, true) || strcmp(com_token, "\n"))
3577 Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
3583 Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
3589 // look for linebreak or }
3590 while(COM_ParseToken(&text, true) && strcmp(com_token, "\n") && strcmp(com_token, "}"));
3591 // break out to top level if it was }
3592 if (!strcmp(com_token, "}"))
3596 // add shader to list (shadername and flags)
3597 // actually here we just poke into the texture settings
3598 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3599 if (!strcmp(out->name, shadername))
3600 out->surfaceparms = flags;
3604 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3615 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3617 if (out->surfaceparms == -1)
3620 Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3621 out->surfaceparms = 0;
3622 // these are defaults
3623 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3624 || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3625 out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3626 if (!strncmp(out->name, "textures/skies/", 15))
3627 out->surfaceparms |= Q3SURFACEPARM_SKY;
3628 if (R_TextureHasAlpha(out->skin.base))
3629 out->surfaceparms |= Q3SURFACEPARM_TRANS;
3632 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3635 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3641 in = (void *)(mod_base + l->fileofs);
3642 if (l->filelen % sizeof(*in))
3643 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3644 count = l->filelen / sizeof(*in);
3645 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3647 loadmodel->brushq3.data_planes = out;
3648 loadmodel->brushq3.num_planes = count;
3650 for (i = 0;i < count;i++, in++, out++)
3652 out->normal[0] = LittleLong(in->normal[0]);
3653 out->normal[1] = LittleLong(in->normal[1]);
3654 out->normal[2] = LittleLong(in->normal[2]);
3655 out->dist = LittleLong(in->dist);
3660 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3663 q3mbrushside_t *out;
3666 in = (void *)(mod_base + l->fileofs);
3667 if (l->filelen % sizeof(*in))
3668 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3669 count = l->filelen / sizeof(*in);
3670 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3672 loadmodel->brushq3.data_brushsides = out;
3673 loadmodel->brushq3.num_brushsides = count;
3675 for (i = 0;i < count;i++, in++, out++)
3677 n = LittleLong(in->planeindex);
3678 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3679 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3680 out->plane = loadmodel->brushq3.data_planes + n;
3681 n = LittleLong(in->textureindex);
3682 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3683 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3684 out->texture = loadmodel->brushq3.data_textures + n;
3688 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3692 int i, j, n, c, count, maxplanes;
3694 winding_t *temp1, *temp2;
3696 in = (void *)(mod_base + l->fileofs);
3697 if (l->filelen % sizeof(*in))
3698 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3699 count = l->filelen / sizeof(*in);
3700 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3702 loadmodel->brushq3.data_brushes = out;
3703 loadmodel->brushq3.num_brushes = count;
3705 temp1 = Winding_New(64);
3706 temp2 = Winding_New(64);
3711 for (i = 0;i < count;i++, in++, out++)
3713 n = LittleLong(in->firstbrushside);
3714 c = LittleLong(in->numbrushsides);
3715 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3716 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3717 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3718 out->numbrushsides = c;
3719 n = LittleLong(in->textureindex);
3720 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3721 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3722 out->texture = loadmodel->brushq3.data_textures + n;
3724 // make a list of mplane_t structs to construct a colbrush from
3725 if (maxplanes < out->numbrushsides)
3727 maxplanes = out->numbrushsides;
3730 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3732 for (j = 0;j < out->numbrushsides;j++)
3734 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3735 planes[j].dist = out->firstbrushside[j].plane->dist;
3737 // make the colbrush from the planes
3738 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
3742 Winding_Free(temp1);
3743 Winding_Free(temp2);
3746 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3752 in = (void *)(mod_base + l->fileofs);
3753 if (l->filelen % sizeof(*in))
3754 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3755 count = l->filelen / sizeof(*in);
3756 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3758 loadmodel->brushq3.data_effects = out;
3759 loadmodel->brushq3.num_effects = count;
3761 for (i = 0;i < count;i++, in++, out++)
3763 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3764 n = LittleLong(in->brushindex);
3765 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3766 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3767 out->brush = loadmodel->brushq3.data_brushes + n;
3768 out->unknown = LittleLong(in->unknown);
3772 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3777 in = (void *)(mod_base + l->fileofs);
3778 if (l->filelen % sizeof(*in))
3779 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3780 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3781 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3782 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3783 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3784 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3785 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3786 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3787 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3789 for (i = 0;i < count;i++, in++)
3791 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3792 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3793 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3794 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3795 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3796 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3797 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3798 // svector/tvector are calculated later in face loading
3799 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3800 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3801 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3802 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3803 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3804 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3805 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3806 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3807 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3808 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3809 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3810 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3811 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3815 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3821 in = (void *)(mod_base + l->fileofs);
3822 if (l->filelen % sizeof(int[3]))
3823 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3824 count = l->filelen / sizeof(*in);
3825 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3827 loadmodel->brushq3.num_triangles = count / 3;
3828 loadmodel->brushq3.data_element3i = out;
3829 loadmodel->brushq3.data_neighbor3i = out + count;
3831 for (i = 0;i < count;i++, in++, out++)
3833 *out = LittleLong(*in);
3834 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3836 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3842 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3850 in = (void *)(mod_base + l->fileofs);
3851 if (l->filelen % sizeof(*in))
3852 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3853 count = l->filelen / sizeof(*in);
3854 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3856 loadmodel->brushq3.data_lightmaps = out;
3857 loadmodel->brushq3.num_lightmaps = count;
3859 for (i = 0;i < count;i++, in++, out++)
3860 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3863 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3867 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3868 //int *originalelement3i;
3869 //int *originalneighbor3i;
3870 float *originalvertex3f;
3871 //float *originalsvector3f;
3872 //float *originaltvector3f;
3873 //float *originalnormal3f;
3874 float *originalcolor4f;
3875 float *originaltexcoordtexture2f;
3876 float *originaltexcoordlightmap2f;
3879 in = (void *)(mod_base + l->fileofs);
3880 if (l->filelen % sizeof(*in))
3881 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3882 count = l->filelen / sizeof(*in);
3883 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3885 loadmodel->brushq3.data_faces = out;
3886 loadmodel->brushq3.num_faces = count;
3888 for (i = 0;i < count;i++, in++, out++)
3890 // check face type first
3891 out->type = LittleLong(in->type);
3892 if (out->type != Q3FACETYPE_POLYGON
3893 && out->type != Q3FACETYPE_PATCH
3894 && out->type != Q3FACETYPE_MESH
3895 && out->type != Q3FACETYPE_FLARE)
3897 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3898 out->num_vertices = 0;
3899 out->num_triangles = 0;
3900 out->type = 0; // error
3904 n = LittleLong(in->textureindex);
3905 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3907 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3908 out->num_vertices = 0;
3909 out->num_triangles = 0;
3910 out->type = 0; // error
3914 out->texture = loadmodel->brushq3.data_textures + n;
3915 n = LittleLong(in->effectindex);
3916 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3918 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3924 out->effect = loadmodel->brushq3.data_effects + n;
3925 n = LittleLong(in->lightmapindex);
3926 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3928 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3932 out->lightmaptexture = NULL;
3934 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3936 out->firstvertex = LittleLong(in->firstvertex);
3937 out->num_vertices = LittleLong(in->numvertices);
3938 out->firstelement = LittleLong(in->firstelement);
3939 out->num_triangles = LittleLong(in->numelements) / 3;
3940 if (out->num_triangles * 3 != LittleLong(in->numelements))
3942 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, LittleLong(in->numelements));
3943 out->num_vertices = 0;
3944 out->num_triangles = 0;
3945 out->type = 0; // error
3948 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3950 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->num_vertices, loadmodel->brushq3.num_vertices);
3951 out->num_vertices = 0;
3952 out->num_triangles = 0;
3953 out->type = 0; // error
3956 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3958 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->num_triangles * 3, loadmodel->brushq3.num_triangles * 3);
3959 out->num_vertices = 0;
3960 out->num_triangles = 0;
3961 out->type = 0; // error
3966 case Q3FACETYPE_POLYGON:
3967 case Q3FACETYPE_MESH:
3968 // no processing necessary
3969 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3970 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3971 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3972 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3973 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3974 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3975 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3976 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3977 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3979 case Q3FACETYPE_PATCH:
3980 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3981 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3982 if (patchsize[0] < 1 || patchsize[1] < 1)
3984 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3985 out->num_vertices = 0;
3986 out->num_triangles = 0;
3987 out->type = 0; // error
3990 // convert patch to Q3FACETYPE_MESH
3991 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3992 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3993 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3994 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3995 finalvertices = finalwidth * finalheight;
3996 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3997 originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3998 //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3999 //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4000 //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4001 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4002 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4003 originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4004 //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
4005 //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4007 originalvertex3f = out->data_vertex3f;
4008 //originalsvector3f = out->data_svector3f;
4009 //originaltvector3f = out->data_tvector3f;
4010 //originalnormal3f = out->data_normal3f;
4011 originalcolor4f = out->data_color4f;
4012 originaltexcoordtexture2f = out->data_texcoordtexture2f;
4013 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
4014 //originalelement3i = out->data_element3i;
4015 //originalneighbor3i = out->data_neighbor3i;
4017 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
4018 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
4019 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
4020 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
4021 out->data_color4f = out->data_normal3f + finalvertices * 3;
4022 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
4023 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
4024 out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
4025 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
4026 out->type = Q3FACETYPE_MESH;
4027 out->firstvertex = -1;
4028 out->num_vertices = finalvertices;
4029 out->firstelement = -1;
4030 out->num_triangles = finaltriangles;
4031 // generate geometry
4032 // (note: normals are skipped because they get recalculated)
4033 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
4034 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
4035 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
4036 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
4037 // generate elements
4038 e = out->data_element3i;
4039 for (y = 0;y < finalheight - 1;y++)
4041 row0 = (y + 0) * finalwidth;
4042 row1 = (y + 1) * finalwidth;
4043 for (x = 0;x < finalwidth - 1;x++)
4055 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
4056 if (developer.integer)
4058 if (out->num_triangles < finaltriangles)
4059 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->num_vertices, finaltriangles, finaltriangles - out->num_triangles, out->num_triangles);
4061 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_vertices, out->num_triangles);
4063 // q3map does not put in collision brushes for curves... ugh
4064 out->collisions = true;
4066 case Q3FACETYPE_FLARE:
4067 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4069 out->num_vertices = 0;
4070 out->num_triangles = 0;
4074 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4075 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4077 if (invalidelements)
4079 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->num_vertices, out->firstelement, out->num_triangles * 3);
4080 for (j = 0;j < out->num_triangles * 3;j++)
4082 Con_Printf(" %i", out->data_element3i[j]);
4083 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4084 out->data_element3i[j] = 0;
4088 // for shadow volumes
4089 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
4090 // for per pixel lighting
4091 Mod_BuildTextureVectorsAndNormals(out->num_vertices, out->num_triangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
4092 // calculate a bounding box
4093 VectorClear(out->mins);
4094 VectorClear(out->maxs);
4095 if (out->num_vertices)
4097 VectorCopy(out->data_vertex3f, out->mins);
4098 VectorCopy(out->data_vertex3f, out->maxs);
4099 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
4101 out->mins[0] = min(out->mins[0], v[0]);
4102 out->maxs[0] = max(out->maxs[0], v[0]);
4103 out->mins[1] = min(out->mins[1], v[1]);
4104 out->maxs[1] = max(out->maxs[1], v[1]);
4105 out->mins[2] = min(out->mins[2], v[2]);
4106 out->maxs[2] = max(out->maxs[2], v[2]);
4108 out->mins[0] -= 1.0f;
4109 out->mins[1] -= 1.0f;
4110 out->mins[2] -= 1.0f;
4111 out->maxs[0] += 1.0f;
4112 out->maxs[1] += 1.0f;
4113 out->maxs[2] += 1.0f;
4117 // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
4120 int totalverts, totaltris;
4121 int originalnum_vertices;
4122 float *originaldata_vertex3f;
4123 float *originaldata_texcoordtexture2f;
4124 float *originaldata_texcoordlightmap2f;
4125 float *originaldata_svector3f;
4126 float *originaldata_tvector3f;
4127 float *originaldata_normal3f;
4128 float *originaldata_color4f;
4129 int originalnum_triangles;
4130 int *originaldata_element3i;
4131 int *originaldata_neighbor3i;
4135 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4139 totalverts += out->num_vertices;
4140 totaltris += out->num_triangles;
4143 originalnum_vertices = loadmodel->brushq3.num_vertices;
4144 originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
4145 originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
4146 originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
4147 originaldata_svector3f = loadmodel->brushq3.data_svector3f;
4148 originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
4149 originaldata_normal3f = loadmodel->brushq3.data_normal3f;
4150 originaldata_color4f = loadmodel->brushq3.data_color4f;
4151 originalnum_triangles = loadmodel->brushq3.num_triangles;
4152 originaldata_element3i = loadmodel->brushq3.data_element3i;
4153 originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
4154 loadmodel->brushq3.num_vertices = totalverts;
4155 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
4156 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
4157 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
4158 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
4159 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
4160 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
4161 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
4162 loadmodel->brushq3.num_triangles = totaltris;
4163 loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
4164 loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
4167 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4171 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
4172 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
4173 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
4174 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
4175 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
4176 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
4177 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
4178 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
4179 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
4180 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
4181 if (out->firstvertex == -1)
4182 Mem_Free(out->data_vertex3f);
4183 if (out->firstelement == -1)
4184 Mem_Free(out->data_element3i);
4185 out->firstvertex = totalverts;
4186 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4187 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4188 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4189 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4190 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4191 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4192 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4193 out->firstelement = totaltris * 3;
4194 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4195 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4196 //for (j = 0;j < out->numtriangles * 3;j++)
4197 // out->data_element3i[j] += totalverts - out->firstvertex;
4198 totalverts += out->num_vertices;
4199 totaltris += out->num_triangles;
4201 Mem_Free(originaldata_vertex3f);
4202 Mem_Free(originaldata_element3i);
4207 static void Mod_Q3BSP_LoadModels(lump_t *l)
4211 int i, j, n, c, count;
4213 in = (void *)(mod_base + l->fileofs);
4214 if (l->filelen % sizeof(*in))
4215 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4216 count = l->filelen / sizeof(*in);
4217 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4219 loadmodel->brushq3.data_models = out;
4220 loadmodel->brushq3.num_models = count;
4222 for (i = 0;i < count;i++, in++, out++)
4224 for (j = 0;j < 3;j++)
4226 out->mins[j] = LittleFloat(in->mins[j]);
4227 out->maxs[j] = LittleFloat(in->maxs[j]);
4229 n = LittleLong(in->firstface);
4230 c = LittleLong(in->numfaces);
4231 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4232 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4233 out->firstface = loadmodel->brushq3.data_faces + n;
4235 n = LittleLong(in->firstbrush);
4236 c = LittleLong(in->numbrushes);
4237 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4238 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4239 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4240 out->numbrushes = c;
4244 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4250 in = (void *)(mod_base + l->fileofs);
4251 if (l->filelen % sizeof(*in))
4252 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4253 count = l->filelen / sizeof(*in);
4254 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4256 loadmodel->brushq3.data_leafbrushes = out;
4257 loadmodel->brushq3.num_leafbrushes = count;
4259 for (i = 0;i < count;i++, in++, out++)
4261 n = LittleLong(*in);
4262 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4263 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4264 *out = loadmodel->brushq3.data_brushes + n;
4268 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4274 in = (void *)(mod_base + l->fileofs);
4275 if (l->filelen % sizeof(*in))
4276 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4277 count = l->filelen / sizeof(*in);
4278 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4280 loadmodel->brushq3.data_leaffaces = out;
4281 loadmodel->brushq3.num_leaffaces = count;
4283 for (i = 0;i < count;i++, in++, out++)
4285 n = LittleLong(*in);
4286 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4287 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4288 *out = loadmodel->brushq3.data_faces + n;
4292 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4296 int i, j, n, c, count;
4298 in = (void *)(mod_base + l->fileofs);
4299 if (l->filelen % sizeof(*in))
4300 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4301 count = l->filelen / sizeof(*in);
4302 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4304 loadmodel->brushq3.data_leafs = out;
4305 loadmodel->brushq3.num_leafs = count;
4307 for (i = 0;i < count;i++, in++, out++)
4309 out->isnode = false;
4311 out->clusterindex = LittleLong(in->clusterindex);
4312 out->areaindex = LittleLong(in->areaindex);
4313 for (j = 0;j < 3;j++)
4315 // yes the mins/maxs are ints
4316 out->mins[j] = LittleLong(in->mins[j]);
4317 out->maxs[j] = LittleLong(in->maxs[j]);
4319 n = LittleLong(in->firstleafface);
4320 c = LittleLong(in->numleaffaces);
4321 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4322 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4323 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4324 out->numleaffaces = c;
4325 n = LittleLong(in->firstleafbrush);
4326 c = LittleLong(in->numleafbrushes);
4327 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4328 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4329 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4330 out->numleafbrushes = c;
4334 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4337 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4338 node->parent = parent;
4341 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4342 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4346 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4352 in = (void *)(mod_base + l->fileofs);
4353 if (l->filelen % sizeof(*in))
4354 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4355 count = l->filelen / sizeof(*in);
4356 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4358 loadmodel->brushq3.data_nodes = out;
4359 loadmodel->brushq3.num_nodes = count;
4361 for (i = 0;i < count;i++, in++, out++)
4365 n = LittleLong(in->planeindex);
4366 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4367 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4368 out->plane = loadmodel->brushq3.data_planes + n;
4369 for (j = 0;j < 2;j++)
4371 n = LittleLong(in->childrenindex[j]);
4374 if (n >= loadmodel->brushq3.num_nodes)
4375 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4376 out->children[j] = loadmodel->brushq3.data_nodes + n;
4381 if (n >= loadmodel->brushq3.num_leafs)
4382 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4383 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4386 for (j = 0;j < 3;j++)
4388 // yes the mins/maxs are ints
4389 out->mins[j] = LittleLong(in->mins[j]);
4390 out->maxs[j] = LittleLong(in->maxs[j]);
4394 // set the parent pointers
4395 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4398 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4401 q3dlightgrid_t *out;
4404 if (l->filelen == 0)
4407 in = (void *)(mod_base + l->fileofs);
4408 if (l->filelen % sizeof(*in))
4409 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4410 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4411 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4412 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4413 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4414 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4415 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4416 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4417 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4418 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4419 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4420 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4421 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4422 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4423 if (l->filelen < count * (int)sizeof(*in))
4424 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]);
4425 if (l->filelen != count * (int)sizeof(*in))
4426 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4428 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4429 loadmodel->brushq3.data_lightgrid = out;
4430 loadmodel->brushq3.num_lightgrid = count;
4432 // no swapping or validation necessary
4433 memcpy(out, in, count * (int)sizeof(*out));
4435 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]);
4436 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]);
4439 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4444 if (l->filelen == 0)
4447 in = (void *)(mod_base + l->fileofs);
4449 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4451 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4452 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4453 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4454 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4455 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4456 if (l->filelen < totalchains + (int)sizeof(*in))
4457 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);
4459 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4460 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4463 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4465 // FIXME: finish this code
4466 VectorCopy(in, out);
4469 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4471 int i, j, k, index[3];
4472 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4473 q3dlightgrid_t *a, *s;
4474 // FIXME: write this
4475 if (!model->brushq3.num_lightgrid)
4477 ambientcolor[0] = 1;
4478 ambientcolor[1] = 1;
4479 ambientcolor[2] = 1;
4482 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4483 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4484 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4485 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4486 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4487 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4488 index[0] = (int)floor(transformed[0]);
4489 index[1] = (int)floor(transformed[1]);
4490 index[2] = (int)floor(transformed[2]);
4491 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4492 // now lerp the values
4493 VectorClear(diffusenormal);
4494 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4495 for (k = 0;k < 2;k++)
4497 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4498 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4500 for (j = 0;j < 2;j++)
4502 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4503 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4505 for (i = 0;i < 2;i++)
4507 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4508 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4510 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4511 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4512 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4513 pitch = s->diffusepitch * M_PI / 128;
4514 yaw = s->diffuseyaw * M_PI / 128;
4515 sinpitch = sin(pitch);
4516 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4517 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4518 diffusenormal[2] += blend * (cos(pitch));
4519 //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)));
4523 VectorNormalize(diffusenormal);
4524 //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]);
4527 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)
4529 int i, startside, endside;
4530 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4534 if (startfrac >= trace->fraction)
4536 // note: all line fragments past first impact fraction are ignored
4537 while (node->isnode)
4539 // recurse down node sides
4540 dist1 = PlaneDiff(start, node->plane);
4541 dist2 = PlaneDiff(end, node->plane);
4542 startside = dist1 < 0;
4543 endside = dist2 < 0;
4544 if (startside == endside)
4546 // most of the time the line fragment is on one side of the plane
4547 node = node->children[startside];
4551 // line crosses node plane, split the line
4552 midfrac = dist1 / (dist1 - dist2);
4553 VectorLerp(linestart, midfrac, lineend, mid);
4554 // take the near side first
4555 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4556 if (midfrac < trace->fraction)
4557 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4562 segmentmins[0] = min(start[0], end[0]);
4563 segmentmins[1] = min(start[1], end[1]);
4564 segmentmins[2] = min(start[2], end[2]);
4565 segmentmaxs[0] = max(start[0], end[0]);
4566 segmentmaxs[1] = max(start[1], end[1]);
4567 segmentmaxs[2] = max(start[2], end[2]);
4568 leaf = (q3mleaf_t *)node;
4569 for (i = 0;i < leaf->numleafbrushes;i++)
4571 if (startfrac >= trace->fraction)
4573 brush = leaf->firstleafbrush[i]->colbrushf;
4574 if (brush && brush->markframe != markframe)
4576 brush->markframe = markframe;
4577 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4578 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4581 if (mod_q3bsp_curves_collisions.integer)
4583 for (i = 0;i < leaf->numleaffaces;i++)
4585 if (startfrac >= trace->fraction)
4587 face = leaf->firstleafface[i];
4588 if (face->collisions && face->collisionmarkframe != markframe)
4590 face->collisionmarkframe = markframe;
4591 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4592 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4598 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)
4601 float nodesegmentmins[3], nodesegmentmaxs[3];
4605 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4606 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4607 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4608 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4609 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4610 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4611 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4615 // recurse down node sides
4616 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4619 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4620 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4622 else if (sides == 2)
4623 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4625 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4627 dist = node->plane->dist - (1.0f / 8.0f);
4628 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4630 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4632 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4638 dist = node->plane->dist + (1.0f / 8.0f);
4639 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4641 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4643 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4649 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4651 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4653 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4659 leaf = (q3mleaf_t *)node;
4660 for (i = 0;i < leaf->numleafbrushes;i++)
4662 brush = leaf->firstleafbrush[i]->colbrushf;
4663 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4665 brush->markframe = markframe;
4666 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4669 if (mod_q3bsp_curves_collisions.integer)
4671 for (i = 0;i < leaf->numleaffaces;i++)
4673 face = leaf->firstleafface[i];
4674 // 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
4675 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4676 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4682 static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
4685 float segmentmins[3], segmentmaxs[3];
4686 colbrushf_t *thisbrush_start, *thisbrush_end;
4687 matrix4x4_t startmatrix, endmatrix;
4688 static int markframe = 0;
4690 memset(trace, 0, sizeof(*trace));
4691 trace->fraction = 1;
4692 trace->hitsupercontentsmask = hitsupercontentsmask;
4693 Matrix4x4_CreateIdentity(&startmatrix);
4694 Matrix4x4_CreateIdentity(&endmatrix);
4695 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4696 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4697 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4698 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4699 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4700 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4701 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4704 if (model->brushq3.submodel)
4706 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4707 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4708 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4709 if (mod_q3bsp_curves_collisions.integer)
4711 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4713 face = model->brushq3.data_thismodel->firstface + i;
4714 if (face->collisions)
4715 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4720 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4724 // box trace, performed as brush trace
4725 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4726 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4727 if (model->brushq3.submodel)
4729 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4730 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4731 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4732 if (mod_q3bsp_curves_collisions.integer)
4734 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4736 face = model->brushq3.data_thismodel->firstface + i;
4737 if (face->collisions)
4738 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4743 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4748 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)
4755 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4756 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4759 // node - recurse down the BSP tree
4760 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4763 node = node->children[0];
4766 node = node->children[1];
4768 default: // crossing
4769 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4771 node = node->children[1];
4778 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4780 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4783 //Returns PVS data for a given point
4784 //(note: can return NULL)
4785 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4788 Mod_CheckLoaded(model);
4789 node = model->brushq3.data_nodes;
4790 while (node->isnode)
4791 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4792 if (((q3mleaf_t *)node)->clusterindex >= 0)
4793 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4798 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4804 while (node->isnode)
4806 d = PlaneDiff(org, node->plane);
4808 node = node->children[0];
4809 else if (d < -radius)
4810 node = node->children[1];
4813 // go down both sides
4814 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4815 node = node->children[1];
4818 // if this is a leaf with a pvs, accumulate the pvs bits
4819 if (((q3mleaf_t *)node)->clusterindex >= 0)
4821 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4822 for (i = 0;i < pvsbytes;i++)
4823 pvsbuffer[i] |= pvs[i];
4828 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4829 //of the given point.
4830 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4832 int bytes = model->brushq3.num_pvschainlength;
4833 bytes = min(bytes, pvsbufferlength);
4834 memset(pvsbuffer, 0, bytes);
4835 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4840 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4842 int supercontents = 0;
4843 if (nativecontents & Q2CONTENTS_SOLID)
4844 supercontents |= SUPERCONTENTS_SOLID;
4845 if (nativecontents & Q2CONTENTS_WATER)
4846 supercontents |= SUPERCONTENTS_WATER;
4847 if (nativecontents & Q2CONTENTS_SLIME)
4848 supercontents |= SUPERCONTENTS_SLIME;
4849 if (nativecontents & Q2CONTENTS_LAVA)
4850 supercontents |= SUPERCONTENTS_LAVA;
4851 return supercontents;
4854 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4856 int nativecontents = 0;
4857 if (supercontents & SUPERCONTENTS_SOLID)
4858 nativecontents |= Q2CONTENTS_SOLID;
4859 if (supercontents & SUPERCONTENTS_WATER)
4860 nativecontents |= Q2CONTENTS_WATER;
4861 if (supercontents & SUPERCONTENTS_SLIME)
4862 nativecontents |= Q2CONTENTS_SLIME;
4863 if (supercontents & SUPERCONTENTS_LAVA)
4864 nativecontents |= Q2CONTENTS_LAVA;
4865 return nativecontents;
4868 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4869 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4870 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4871 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);
4872 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4875 q3dheader_t *header;
4876 float corner[3], yawradius, modelradius;
4878 mod->type = mod_brushq3;
4882 header = (q3dheader_t *)buffer;
4884 i = LittleLong(header->version);
4885 if (i != Q3BSPVERSION)
4886 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4887 if (loadmodel->isworldmodel)
4889 Cvar_SetValue("halflifebsp", false);
4890 // until we get a texture for it...
4894 mod->soundfromcenter = true;
4895 mod->TraceBox = Mod_Q3BSP_TraceBox;
4896 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4897 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4898 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4899 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4900 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4901 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4902 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4903 //mod->DrawSky = R_Q3BSP_DrawSky;
4904 mod->Draw = R_Q3BSP_Draw;
4905 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4906 mod->DrawLight = R_Q3BSP_DrawLight;
4908 mod_base = (qbyte *)header;
4910 // swap all the lumps
4911 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4912 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4914 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4915 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4916 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4917 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4918 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4919 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4920 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4921 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4922 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4923 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4924 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4925 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4926 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4927 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4928 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4929 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4930 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4931 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4933 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4940 // LordHavoc: only register submodels if it is the world
4941 // (prevents bsp models from replacing world submodels)
4942 if (!loadmodel->isworldmodel)
4944 // duplicate the basic information
4945 sprintf(name, "*%i", i);
4946 mod = Mod_FindName(name);
4948 strcpy(mod->name, name);
4949 // textures and memory belong to the main model
4950 mod->texturepool = NULL;
4951 mod->mempool = NULL;
4953 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4954 mod->brushq3.submodel = i;
4956 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4957 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4958 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4959 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4960 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4961 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4962 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4963 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4964 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4965 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4966 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4967 mod->yawmins[2] = mod->normalmins[2];
4968 mod->yawmaxs[2] = mod->normalmaxs[2];
4969 mod->radius = modelradius;
4970 mod->radius2 = modelradius * modelradius;
4972 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
4973 if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
4975 if (j < mod->brushq3.data_thismodel->numfaces)
4976 mod->DrawSky = R_Q3BSP_DrawSky;
4980 void Mod_IBSP_Load(model_t *mod, void *buffer)
4982 int i = LittleLong(((int *)buffer)[1]);
4983 if (i == Q3BSPVERSION)
4984 Mod_Q3BSP_Load(mod,buffer);
4985 else if (i == Q2BSPVERSION)
4986 Mod_Q2BSP_Load(mod,buffer);
4988 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4991 void Mod_MAP_Load(model_t *mod, void *buffer)
4993 Host_Error("Mod_MAP_Load: not yet implemented\n");