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 #if COLLISIONPARANOID >= 3
687 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]);
689 // trace a line through the generated clipping hull
690 //rhc.boxsupercontents = boxsupercontents;
691 rhc.hull = &box_hull;
693 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
694 rhc.trace->fraction = 1;
695 rhc.trace->allsolid = true;
696 VectorCopy(start, rhc.start);
697 VectorCopy(end, rhc.end);
698 VectorSubtract(rhc.end, rhc.start, rhc.dist);
699 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
700 VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
701 if (rhc.trace->startsupercontents)
702 rhc.trace->startsupercontents = boxsupercontents;
706 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)
708 int side, distz = endz - startz;
713 if (node->contents < 0)
714 return false; // didn't hit anything
716 switch (node->plane->type)
719 node = node->children[x < node->plane->dist];
722 node = node->children[y < node->plane->dist];
725 side = startz < node->plane->dist;
726 if ((endz < node->plane->dist) == side)
728 node = node->children[side];
731 // found an intersection
732 mid = node->plane->dist;
735 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
736 front += startz * node->plane->normal[2];
737 back += endz * node->plane->normal[2];
738 side = front < node->plane->dist;
739 if ((back < node->plane->dist) == side)
741 node = node->children[side];
744 // found an intersection
745 mid = startz + distz * (front - node->plane->dist) / (front - back);
749 // go down front side
750 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
751 return true; // hit something
754 // check for impact on this node
755 if (node->numsurfaces)
760 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
761 for (i = 0;i < node->numsurfaces;i++, surf++)
763 if (!(surf->flags & SURF_LIGHTMAP))
764 continue; // no lightmaps
766 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]);
767 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]);
769 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
772 ds -= surf->texturemins[0];
773 dt -= surf->texturemins[1];
775 if (ds > surf->extents[0] || dt > surf->extents[1])
781 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;
782 line3 = ((surf->extents[0]>>4)+1)*3;
783 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
785 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
787 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
789 scale = d_lightstylevalue[surf->styles[maps]];
790 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
791 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
792 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
793 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
798 LordHavoc: here's the readable version of the interpolation
799 code, not quite as easy for the compiler to optimize...
801 dsfrac is the X position in the lightmap pixel, * 16
802 dtfrac is the Y position in the lightmap pixel, * 16
803 r00 is top left corner, r01 is top right corner
804 r10 is bottom left corner, r11 is bottom right corner
805 g and b are the same layout.
806 r0 and r1 are the top and bottom intermediate results
808 first we interpolate the top two points, to get the top
811 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
812 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
813 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
815 then we interpolate the bottom two points, to get the
818 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
819 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
820 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
822 then we interpolate the top and bottom samples to get the
823 middle sample (the one which was requested)
825 r = (((r1-r0) * dtfrac) >> 4) + r0;
826 g = (((g1-g0) * dtfrac) >> 4) + g0;
827 b = (((b1-b0) * dtfrac) >> 4) + b0;
830 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
831 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
832 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
834 return true; // success
839 node = node->children[side ^ 1];
841 distz = endz - startz;
846 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
848 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);
851 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
858 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
868 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
871 for (c = *in++;c > 0;c--)
875 Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\"\n", loadmodel->name);
884 static void Mod_Q1BSP_LoadTextures(lump_t *l)
886 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
888 texture_t *tx, *tx2, *anims[10], *altanims[10];
890 qbyte *data, *mtdata;
893 loadmodel->brushq1.textures = NULL;
895 // add two slots for notexture walls and notexture liquids
898 m = (dmiptexlump_t *)(mod_base + l->fileofs);
899 m->nummiptex = LittleLong (m->nummiptex);
900 loadmodel->brushq1.numtextures = m->nummiptex + 2;
905 loadmodel->brushq1.numtextures = 2;
908 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
910 // fill out all slots with notexture
911 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
914 strcpy(tx->name, "NO TEXTURE FOUND");
917 tx->skin.base = r_notexture;
918 tx->shader = &Cshader_wall_lightmap;
919 tx->flags = SURF_SOLIDCLIP;
920 if (i == loadmodel->brushq1.numtextures - 1)
922 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
923 tx->shader = &Cshader_water;
925 tx->currentframe = tx;
931 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
933 // LordHavoc: mostly rewritten map texture loader
934 for (i = 0;i < m->nummiptex;i++)
936 dofs[i] = LittleLong(dofs[i]);
937 if (dofs[i] == -1 || r_nosurftextures.integer)
939 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
941 // make sure name is no more than 15 characters
942 for (j = 0;dmiptex->name[j] && j < 15;j++)
943 name[j] = dmiptex->name[j];
946 mtwidth = LittleLong(dmiptex->width);
947 mtheight = LittleLong(dmiptex->height);
949 j = LittleLong(dmiptex->offsets[0]);
953 if (j < 40 || j + mtwidth * mtheight > l->filelen)
955 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
958 mtdata = (qbyte *)dmiptex + j;
961 if ((mtwidth & 15) || (mtheight & 15))
962 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
964 // LordHavoc: force all names to lowercase
965 for (j = 0;name[j];j++)
966 if (name[j] >= 'A' && name[j] <= 'Z')
967 name[j] += 'a' - 'A';
969 tx = loadmodel->brushq1.textures + i;
970 strcpy(tx->name, name);
972 tx->height = mtheight;
976 sprintf(tx->name, "unnamed%i", i);
977 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
980 // LordHavoc: HL sky textures are entirely different than quake
981 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
983 if (loadmodel->isworldmodel)
985 data = loadimagepixels(tx->name, false, 0, 0);
988 if (image_width == 256 && image_height == 128)
996 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
998 R_InitSky(mtdata, 1);
1001 else if (mtdata != NULL)
1002 R_InitSky(mtdata, 1);
1007 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
1009 // did not find external texture, load it from the bsp or wad3
1010 if (loadmodel->brush.ishlbsp)
1012 // internal texture overrides wad
1013 qbyte *pixels, *freepixels, *fogpixels;
1014 pixels = freepixels = NULL;
1016 pixels = W_ConvertWAD3Texture(dmiptex);
1018 pixels = freepixels = W_GetTexture(tx->name);
1021 tx->width = image_width;
1022 tx->height = image_height;
1023 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);
1024 if (Image_CheckAlpha(pixels, image_width * image_height, true))
1026 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
1027 for (j = 0;j < image_width * image_height * 4;j += 4)
1029 fogpixels[j + 0] = 255;
1030 fogpixels[j + 1] = 255;
1031 fogpixels[j + 2] = 255;
1032 fogpixels[j + 3] = pixels[j + 3];
1034 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
1035 Mem_Free(fogpixels);
1039 Mem_Free(freepixels);
1041 else if (mtdata) // texture included
1042 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
1045 if (tx->skin.base == NULL)
1050 tx->skin.base = r_notexture;
1053 if (tx->name[0] == '*')
1055 // turb does not block movement
1056 tx->flags &= ~SURF_SOLIDCLIP;
1057 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
1058 // LordHavoc: some turbulent textures should be fullbright and solid
1059 if (!strncmp(tx->name,"*lava",5)
1060 || !strncmp(tx->name,"*teleport",9)
1061 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
1062 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
1064 tx->flags |= SURF_WATERALPHA;
1065 tx->shader = &Cshader_water;
1067 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
1069 tx->flags |= SURF_DRAWSKY;
1070 tx->shader = &Cshader_sky;
1074 tx->flags |= SURF_LIGHTMAP;
1076 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
1077 tx->shader = &Cshader_wall_lightmap;
1080 // start out with no animation
1081 tx->currentframe = tx;
1084 // sequence the animations
1085 for (i = 0;i < m->nummiptex;i++)
1087 tx = loadmodel->brushq1.textures + i;
1088 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
1090 if (tx->anim_total[0] || tx->anim_total[1])
1091 continue; // already sequenced
1093 // find the number of frames in the animation
1094 memset(anims, 0, sizeof(anims));
1095 memset(altanims, 0, sizeof(altanims));
1097 for (j = i;j < m->nummiptex;j++)
1099 tx2 = loadmodel->brushq1.textures + j;
1100 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
1104 if (num >= '0' && num <= '9')
1105 anims[num - '0'] = tx2;
1106 else if (num >= 'a' && num <= 'j')
1107 altanims[num - 'a'] = tx2;
1109 Con_Printf("Bad animating texture %s\n", tx->name);
1113 for (j = 0;j < 10;j++)
1120 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
1123 for (j = 0;j < max;j++)
1127 Con_Printf("Missing frame %i of %s\n", j, tx->name);
1131 for (j = 0;j < altmax;j++)
1135 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
1144 // if there is no alternate animation, duplicate the primary
1145 // animation into the alternate
1147 for (k = 0;k < 10;k++)
1148 altanims[k] = anims[k];
1151 // link together the primary animation
1152 for (j = 0;j < max;j++)
1155 tx2->animated = true;
1156 tx2->anim_total[0] = max;
1157 tx2->anim_total[1] = altmax;
1158 for (k = 0;k < 10;k++)
1160 tx2->anim_frames[0][k] = anims[k];
1161 tx2->anim_frames[1][k] = altanims[k];
1165 // if there really is an alternate anim...
1166 if (anims[0] != altanims[0])
1168 // link together the alternate animation
1169 for (j = 0;j < altmax;j++)
1172 tx2->animated = true;
1173 // the primary/alternate are reversed here
1174 tx2->anim_total[0] = altmax;
1175 tx2->anim_total[1] = max;
1176 for (k = 0;k < 10;k++)
1178 tx2->anim_frames[0][k] = altanims[k];
1179 tx2->anim_frames[1][k] = anims[k];
1186 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1189 qbyte *in, *out, *data, d;
1190 char litfilename[1024];
1191 loadmodel->brushq1.lightdata = NULL;
1192 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1194 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1195 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1197 else // LordHavoc: bsp version 29 (normal white lighting)
1199 // LordHavoc: hope is not lost yet, check for a .lit file to load
1200 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1201 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1202 strlcat (litfilename, ".lit", sizeof (litfilename));
1203 data = (qbyte*) FS_LoadFile(litfilename, false);
1206 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1208 i = LittleLong(((int *)data)[1]);
1211 Con_DPrintf("loaded %s\n", litfilename);
1212 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1213 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1219 Con_Printf("Unknown .lit file version (%d)\n", i);
1225 if (fs_filesize == 8)
1226 Con_Printf("Empty .lit file, ignoring\n");
1228 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1232 // LordHavoc: oh well, expand the white lighting data
1235 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1236 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1237 out = loadmodel->brushq1.lightdata;
1238 memcpy(in, mod_base + l->fileofs, l->filelen);
1239 for (i = 0;i < l->filelen;i++)
1249 static void Mod_Q1BSP_LoadLightList(void)
1251 int a, n, numlights;
1252 char lightsfilename[1024], *s, *t, *lightsstring;
1255 strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1256 FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1257 strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1258 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1264 while (*s && *s != '\n')
1268 Mem_Free(lightsstring);
1269 Host_Error("lights file must end with a newline\n");
1274 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1277 while (*s && n < numlights)
1280 while (*s && *s != '\n')
1284 Mem_Free(lightsstring);
1285 Host_Error("misparsed lights file!\n");
1287 e = loadmodel->brushq1.lights + n;
1289 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);
1293 Mem_Free(lightsstring);
1294 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);
1301 Mem_Free(lightsstring);
1302 Host_Error("misparsed lights file!\n");
1304 loadmodel->brushq1.numlights = numlights;
1305 Mem_Free(lightsstring);
1309 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1311 loadmodel->brushq1.num_compressedpvs = 0;
1312 loadmodel->brushq1.data_compressedpvs = NULL;
1315 loadmodel->brushq1.num_compressedpvs = l->filelen;
1316 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1317 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1320 // used only for HalfLife maps
1321 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1323 char key[128], value[4096];
1328 if (!COM_ParseToken(&data, false))
1330 if (com_token[0] != '{')
1334 if (!COM_ParseToken(&data, false))
1336 if (com_token[0] == '}')
1337 break; // end of worldspawn
1338 if (com_token[0] == '_')
1339 strcpy(key, com_token + 1);
1341 strcpy(key, com_token);
1342 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1343 key[strlen(key)-1] = 0;
1344 if (!COM_ParseToken(&data, false))
1346 strcpy(value, com_token);
1347 if (!strcmp("wad", key)) // for HalfLife maps
1349 if (loadmodel->brush.ishlbsp)
1352 for (i = 0;i < 4096;i++)
1353 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1359 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1360 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1362 else if (value[i] == ';' || value[i] == 0)
1366 strcpy(wadname, "textures/");
1367 strcat(wadname, &value[j]);
1368 W_LoadTextureWadFile(wadname, false);
1380 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1382 loadmodel->brush.entities = NULL;
1385 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1386 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1387 if (loadmodel->brush.ishlbsp)
1388 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1392 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1398 in = (void *)(mod_base + l->fileofs);
1399 if (l->filelen % sizeof(*in))
1400 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1401 count = l->filelen / sizeof(*in);
1402 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1404 loadmodel->brushq1.vertexes = out;
1405 loadmodel->brushq1.numvertexes = count;
1407 for ( i=0 ; i<count ; i++, in++, out++)
1409 out->position[0] = LittleFloat(in->point[0]);
1410 out->position[1] = LittleFloat(in->point[1]);
1411 out->position[2] = LittleFloat(in->point[2]);
1415 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1421 in = (void *)(mod_base + l->fileofs);
1422 if (l->filelen % sizeof(*in))
1423 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1424 count = l->filelen / sizeof(*in);
1425 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1427 loadmodel->brushq1.submodels = out;
1428 loadmodel->brush.numsubmodels = count;
1430 for ( i=0 ; i<count ; i++, in++, out++)
1432 for (j=0 ; j<3 ; j++)
1434 // spread the mins / maxs by a pixel
1435 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1436 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1437 out->origin[j] = LittleFloat(in->origin[j]);
1439 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1440 out->headnode[j] = LittleLong(in->headnode[j]);
1441 out->visleafs = LittleLong(in->visleafs);
1442 out->firstface = LittleLong(in->firstface);
1443 out->numfaces = LittleLong(in->numfaces);
1447 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1453 in = (void *)(mod_base + l->fileofs);
1454 if (l->filelen % sizeof(*in))
1455 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1456 count = l->filelen / sizeof(*in);
1457 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1459 loadmodel->brushq1.edges = out;
1460 loadmodel->brushq1.numedges = count;
1462 for ( i=0 ; i<count ; i++, in++, out++)
1464 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1465 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1469 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1473 int i, j, k, count, miptex;
1475 in = (void *)(mod_base + l->fileofs);
1476 if (l->filelen % sizeof(*in))
1477 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1478 count = l->filelen / sizeof(*in);
1479 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1481 loadmodel->brushq1.texinfo = out;
1482 loadmodel->brushq1.numtexinfo = count;
1484 for (i = 0;i < count;i++, in++, out++)
1486 for (k = 0;k < 2;k++)
1487 for (j = 0;j < 4;j++)
1488 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1490 miptex = LittleLong(in->miptex);
1491 out->flags = LittleLong(in->flags);
1493 out->texture = NULL;
1494 if (loadmodel->brushq1.textures)
1496 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1497 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1499 out->texture = loadmodel->brushq1.textures + miptex;
1501 if (out->flags & TEX_SPECIAL)
1503 // if texture chosen is NULL or the shader needs a lightmap,
1504 // force to notexture water shader
1505 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1506 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1510 // if texture chosen is NULL, force to notexture
1511 if (out->texture == NULL)
1512 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1518 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1523 mins[0] = mins[1] = mins[2] = 9999;
1524 maxs[0] = maxs[1] = maxs[2] = -9999;
1526 for (i = 0;i < numverts;i++)
1528 for (j = 0;j < 3;j++, v++)
1538 #define MAX_SUBDIVPOLYTRIANGLES 4096
1539 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1541 static int subdivpolyverts, subdivpolytriangles;
1542 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1543 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1545 static int subdivpolylookupvert(vec3_t v)
1548 for (i = 0;i < subdivpolyverts;i++)
1549 if (subdivpolyvert[i][0] == v[0]
1550 && subdivpolyvert[i][1] == v[1]
1551 && subdivpolyvert[i][2] == v[2])
1553 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1554 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1555 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1556 return subdivpolyverts++;
1559 static void SubdividePolygon(int numverts, float *verts)
1561 int i, i1, i2, i3, f, b, c, p;
1562 vec3_t mins, maxs, front[256], back[256];
1563 float m, *pv, *cv, dist[256], frac;
1566 Host_Error("SubdividePolygon: ran out of verts in buffer");
1568 BoundPoly(numverts, verts, mins, maxs);
1570 for (i = 0;i < 3;i++)
1572 m = (mins[i] + maxs[i]) * 0.5;
1573 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1574 if (maxs[i] - m < 8)
1576 if (m - mins[i] < 8)
1580 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1581 dist[c] = cv[i] - m;
1584 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1588 VectorCopy(pv, front[f]);
1593 VectorCopy(pv, back[b]);
1596 if (dist[p] == 0 || dist[c] == 0)
1598 if ((dist[p] > 0) != (dist[c] > 0) )
1601 frac = dist[p] / (dist[p] - dist[c]);
1602 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1603 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1604 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1610 SubdividePolygon(f, front[0]);
1611 SubdividePolygon(b, back[0]);
1615 i1 = subdivpolylookupvert(verts);
1616 i2 = subdivpolylookupvert(verts + 3);
1617 for (i = 2;i < numverts;i++)
1619 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1621 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1625 i3 = subdivpolylookupvert(verts + i * 3);
1626 subdivpolyindex[subdivpolytriangles][0] = i1;
1627 subdivpolyindex[subdivpolytriangles][1] = i2;
1628 subdivpolyindex[subdivpolytriangles][2] = i3;
1630 subdivpolytriangles++;
1634 //Breaks a polygon up along axial 64 unit
1635 //boundaries so that turbulent and sky warps
1636 //can be done reasonably.
1637 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1643 subdivpolytriangles = 0;
1644 subdivpolyverts = 0;
1645 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1646 if (subdivpolytriangles < 1)
1647 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1649 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1650 mesh->num_vertices = subdivpolyverts;
1651 mesh->num_triangles = subdivpolytriangles;
1652 mesh->vertex = (surfvertex_t *)(mesh + 1);
1653 mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1654 memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1656 for (i = 0;i < mesh->num_triangles;i++)
1657 for (j = 0;j < 3;j++)
1658 mesh->index[i*3+j] = subdivpolyindex[i][j];
1660 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1662 VectorCopy(subdivpolyvert[i], v->v);
1663 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1664 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1669 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1672 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1673 mesh->num_vertices = numverts;
1674 mesh->num_triangles = numtriangles;
1675 mesh->data_vertex3f = (float *)(mesh + 1);
1676 mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1677 mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1678 mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1679 mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1680 mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1681 mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1682 mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1683 mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1684 mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1688 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1691 float *vec, *vert, mins[3], maxs[3], val, *v;
1694 // convert edges back to a normal polygon
1695 surf->poly_numverts = numedges;
1696 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1697 for (i = 0;i < numedges;i++)
1699 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1701 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1703 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1704 VectorCopy(vec, vert);
1708 // calculate polygon bounding box and center
1709 vert = surf->poly_verts;
1710 VectorCopy(vert, mins);
1711 VectorCopy(vert, maxs);
1713 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1715 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1716 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1717 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1719 VectorCopy(mins, surf->poly_mins);
1720 VectorCopy(maxs, surf->poly_maxs);
1721 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1722 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1723 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1725 // generate surface extents information
1726 tex = surf->texinfo;
1727 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1728 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1729 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1731 for (j = 0;j < 2;j++)
1733 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1740 for (i = 0;i < 2;i++)
1742 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1743 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1747 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1751 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1755 in = (void *)(mod_base + l->fileofs);
1756 if (l->filelen % sizeof(*in))
1757 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1758 count = l->filelen / sizeof(*in);
1759 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1761 loadmodel->brushq1.numsurfaces = count;
1762 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1763 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1764 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1766 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++)
1768 surf->number = surfnum;
1769 // FIXME: validate edges, texinfo, etc?
1770 firstedge = LittleLong(in->firstedge);
1771 numedges = LittleShort(in->numedges);
1772 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)
1773 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1774 i = LittleShort(in->texinfo);
1775 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1776 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1777 surf->texinfo = loadmodel->brushq1.texinfo + i;
1778 surf->flags = surf->texinfo->texture->flags;
1780 planenum = LittleShort(in->planenum);
1781 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1782 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1784 if (LittleShort(in->side))
1785 surf->flags |= SURF_PLANEBACK;
1787 surf->plane = loadmodel->brushq1.planes + planenum;
1789 // clear lightmap (filled in later)
1790 surf->lightmaptexture = NULL;
1792 // force lightmap upload on first time seeing the surface
1793 surf->cached_dlight = true;
1795 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1797 ssize = (surf->extents[0] >> 4) + 1;
1798 tsize = (surf->extents[1] >> 4) + 1;
1801 for (i = 0;i < MAXLIGHTMAPS;i++)
1802 surf->styles[i] = in->styles[i];
1803 i = LittleLong(in->lightofs);
1805 surf->samples = NULL;
1806 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1807 surf->samples = loadmodel->brushq1.lightdata + i;
1808 else // LordHavoc: white lighting (bsp version 29)
1809 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1811 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1813 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1814 Host_Error("Bad surface extents");
1815 // stainmap for permanent marks on walls
1816 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1818 memset(surf->stainsamples, 255, ssize * tsize * 3);
1822 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1824 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++)
1827 mesh->num_vertices = surf->poly_numverts;
1828 mesh->num_triangles = surf->poly_numverts - 2;
1829 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1830 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1831 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1832 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1833 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1834 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1835 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1836 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1837 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1838 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1840 surf->lightmaptexturestride = 0;
1841 surf->lightmaptexture = NULL;
1843 for (i = 0;i < mesh->num_vertices;i++)
1845 mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1846 mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1847 mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1848 s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1849 t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1850 mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1851 mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1852 mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1853 mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1854 mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1855 mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1856 mesh->data_lightmapoffsets[i] = 0;
1859 for (i = 0;i < mesh->num_triangles;i++)
1861 mesh->data_element3i[i * 3 + 0] = 0;
1862 mesh->data_element3i[i * 3 + 1] = i + 1;
1863 mesh->data_element3i[i * 3 + 2] = i + 2;
1866 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1867 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);
1869 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1871 int i, iu, iv, smax, tmax;
1872 float u, v, ubase, vbase, uscale, vscale;
1874 smax = surf->extents[0] >> 4;
1875 tmax = surf->extents[1] >> 4;
1877 surf->flags |= SURF_LIGHTMAP;
1878 if (r_miplightmaps.integer)
1880 surf->lightmaptexturestride = smax+1;
1881 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);
1885 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1886 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);
1888 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1889 uscale = (uscale - ubase) / (smax + 1);
1890 vscale = (vscale - vbase) / (tmax + 1);
1892 for (i = 0;i < mesh->num_vertices;i++)
1894 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1895 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1896 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1897 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1898 // LordHavoc: calc lightmap data offset for vertex lighting to use
1901 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1907 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1909 node->parent = parent;
1910 if (node->contents < 0)
1912 Mod_Q1BSP_SetParent(node->children[0], node);
1913 Mod_Q1BSP_SetParent(node->children[1], node);
1916 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1922 in = (void *)(mod_base + l->fileofs);
1923 if (l->filelen % sizeof(*in))
1924 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1925 count = l->filelen / sizeof(*in);
1926 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1928 loadmodel->brushq1.nodes = out;
1929 loadmodel->brushq1.numnodes = count;
1931 for ( i=0 ; i<count ; i++, in++, out++)
1933 for (j=0 ; j<3 ; j++)
1935 out->mins[j] = LittleShort(in->mins[j]);
1936 out->maxs[j] = LittleShort(in->maxs[j]);
1939 p = LittleLong(in->planenum);
1940 out->plane = loadmodel->brushq1.planes + p;
1942 out->firstsurface = LittleShort(in->firstface);
1943 out->numsurfaces = LittleShort(in->numfaces);
1945 for (j=0 ; j<2 ; j++)
1947 p = LittleShort(in->children[j]);
1949 out->children[j] = loadmodel->brushq1.nodes + p;
1951 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1955 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1958 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1962 int i, j, count, p, pvschainbytes;
1965 in = (void *)(mod_base + l->fileofs);
1966 if (l->filelen % sizeof(*in))
1967 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1968 count = l->filelen / sizeof(*in);
1969 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1971 loadmodel->brushq1.leafs = out;
1972 loadmodel->brushq1.numleafs = count;
1973 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1974 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1976 for ( i=0 ; i<count ; i++, in++, out++)
1978 for (j=0 ; j<3 ; j++)
1980 out->mins[j] = LittleShort(in->mins[j]);
1981 out->maxs[j] = LittleShort(in->maxs[j]);
1984 // FIXME: this function could really benefit from some error checking
1986 out->contents = LittleLong(in->contents);
1988 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1989 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1990 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
1992 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);
1993 out->firstmarksurface = NULL;
1994 out->nummarksurfaces = 0;
1998 memset(out->pvsdata, 0xFF, pvschainbytes);
1999 pvs += pvschainbytes;
2001 p = LittleLong(in->visofs);
2004 if (p >= loadmodel->brushq1.num_compressedpvs)
2005 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
2007 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
2010 for (j = 0;j < 4;j++)
2011 out->ambient_sound_level[j] = in->ambient_level[j];
2013 // FIXME: Insert caustics here
2017 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
2019 dclipnode_t *in, *out;
2023 in = (void *)(mod_base + l->fileofs);
2024 if (l->filelen % sizeof(*in))
2025 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2026 count = l->filelen / sizeof(*in);
2027 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2029 loadmodel->brushq1.clipnodes = out;
2030 loadmodel->brushq1.numclipnodes = count;
2032 if (loadmodel->brush.ishlbsp)
2034 hull = &loadmodel->brushq1.hulls[1];
2035 hull->clipnodes = out;
2036 hull->firstclipnode = 0;
2037 hull->lastclipnode = count-1;
2038 hull->planes = loadmodel->brushq1.planes;
2039 hull->clip_mins[0] = -16;
2040 hull->clip_mins[1] = -16;
2041 hull->clip_mins[2] = -36;
2042 hull->clip_maxs[0] = 16;
2043 hull->clip_maxs[1] = 16;
2044 hull->clip_maxs[2] = 36;
2045 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2047 hull = &loadmodel->brushq1.hulls[2];
2048 hull->clipnodes = out;
2049 hull->firstclipnode = 0;
2050 hull->lastclipnode = count-1;
2051 hull->planes = loadmodel->brushq1.planes;
2052 hull->clip_mins[0] = -32;
2053 hull->clip_mins[1] = -32;
2054 hull->clip_mins[2] = -32;
2055 hull->clip_maxs[0] = 32;
2056 hull->clip_maxs[1] = 32;
2057 hull->clip_maxs[2] = 32;
2058 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2060 hull = &loadmodel->brushq1.hulls[3];
2061 hull->clipnodes = out;
2062 hull->firstclipnode = 0;
2063 hull->lastclipnode = count-1;
2064 hull->planes = loadmodel->brushq1.planes;
2065 hull->clip_mins[0] = -16;
2066 hull->clip_mins[1] = -16;
2067 hull->clip_mins[2] = -18;
2068 hull->clip_maxs[0] = 16;
2069 hull->clip_maxs[1] = 16;
2070 hull->clip_maxs[2] = 18;
2071 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2075 hull = &loadmodel->brushq1.hulls[1];
2076 hull->clipnodes = out;
2077 hull->firstclipnode = 0;
2078 hull->lastclipnode = count-1;
2079 hull->planes = loadmodel->brushq1.planes;
2080 hull->clip_mins[0] = -16;
2081 hull->clip_mins[1] = -16;
2082 hull->clip_mins[2] = -24;
2083 hull->clip_maxs[0] = 16;
2084 hull->clip_maxs[1] = 16;
2085 hull->clip_maxs[2] = 32;
2086 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2088 hull = &loadmodel->brushq1.hulls[2];
2089 hull->clipnodes = out;
2090 hull->firstclipnode = 0;
2091 hull->lastclipnode = count-1;
2092 hull->planes = loadmodel->brushq1.planes;
2093 hull->clip_mins[0] = -32;
2094 hull->clip_mins[1] = -32;
2095 hull->clip_mins[2] = -24;
2096 hull->clip_maxs[0] = 32;
2097 hull->clip_maxs[1] = 32;
2098 hull->clip_maxs[2] = 64;
2099 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2102 for (i=0 ; i<count ; i++, out++, in++)
2104 out->planenum = LittleLong(in->planenum);
2105 out->children[0] = LittleShort(in->children[0]);
2106 out->children[1] = LittleShort(in->children[1]);
2107 if (out->children[0] >= count || out->children[1] >= count)
2108 Host_Error("Corrupt clipping hull(out of range child)\n");
2112 //Duplicate the drawing hull structure as a clipping hull
2113 static void Mod_Q1BSP_MakeHull0(void)
2120 hull = &loadmodel->brushq1.hulls[0];
2122 in = loadmodel->brushq1.nodes;
2123 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
2125 hull->clipnodes = out;
2126 hull->firstclipnode = 0;
2127 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
2128 hull->planes = loadmodel->brushq1.planes;
2130 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
2132 out->planenum = in->plane - loadmodel->brushq1.planes;
2133 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
2134 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
2138 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
2143 in = (void *)(mod_base + l->fileofs);
2144 if (l->filelen % sizeof(*in))
2145 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2146 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2147 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2149 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2151 j = (unsigned) LittleShort(in[i]);
2152 if (j >= loadmodel->brushq1.numsurfaces)
2153 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2154 loadmodel->brushq1.marksurfaces[i] = j;
2158 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2163 in = (void *)(mod_base + l->fileofs);
2164 if (l->filelen % sizeof(*in))
2165 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2166 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2167 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2169 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2170 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2174 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2180 in = (void *)(mod_base + l->fileofs);
2181 if (l->filelen % sizeof(*in))
2182 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2184 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2185 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2187 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2189 out->normal[0] = LittleFloat(in->normal[0]);
2190 out->normal[1] = LittleFloat(in->normal[1]);
2191 out->normal[2] = LittleFloat(in->normal[2]);
2192 out->dist = LittleFloat(in->dist);
2198 typedef struct portal_s
2201 mnode_t *nodes[2]; // [0] = front side of plane
2202 struct portal_s *next[2];
2204 struct portal_s *chain; // all portals are linked into a list
2208 static portal_t *portalchain;
2215 static portal_t *AllocPortal(void)
2218 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2219 p->chain = portalchain;
2224 static void FreePortal(portal_t *p)
2229 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2231 // calculate children first
2232 if (node->children[0]->contents >= 0)
2233 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2234 if (node->children[1]->contents >= 0)
2235 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2237 // make combined bounding box from children
2238 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2239 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2240 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2241 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2242 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2243 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2246 static void Mod_Q1BSP_FinalizePortals(void)
2248 int i, j, numportals, numpoints;
2249 portal_t *p, *pnext;
2252 mleaf_t *leaf, *endleaf;
2255 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2256 leaf = loadmodel->brushq1.leafs;
2257 endleaf = leaf + loadmodel->brushq1.numleafs;
2258 for (;leaf < endleaf;leaf++)
2260 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2261 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2268 for (i = 0;i < 2;i++)
2270 leaf = (mleaf_t *)p->nodes[i];
2272 for (j = 0;j < w->numpoints;j++)
2274 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2275 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2276 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2277 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2278 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2279 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2286 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2288 // tally up portal and point counts
2294 // note: this check must match the one below or it will usually corrupt memory
2295 // 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
2296 if (p->winding && p->nodes[0] != p->nodes[1]
2297 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2298 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2301 numpoints += p->winding->numpoints * 2;
2305 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2306 loadmodel->brushq1.numportals = numportals;
2307 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2308 loadmodel->brushq1.numportalpoints = numpoints;
2309 // clear all leaf portal chains
2310 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2311 loadmodel->brushq1.leafs[i].portals = NULL;
2312 // process all portals in the global portal chain, while freeing them
2313 portal = loadmodel->brushq1.portals;
2314 point = loadmodel->brushq1.portalpoints;
2323 // note: this check must match the one above or it will usually corrupt memory
2324 // 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
2325 if (p->nodes[0] != p->nodes[1]
2326 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2327 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2329 // first make the back to front portal(forward portal)
2330 portal->points = point;
2331 portal->numpoints = p->winding->numpoints;
2332 portal->plane.dist = p->plane.dist;
2333 VectorCopy(p->plane.normal, portal->plane.normal);
2334 portal->here = (mleaf_t *)p->nodes[1];
2335 portal->past = (mleaf_t *)p->nodes[0];
2337 for (j = 0;j < portal->numpoints;j++)
2339 VectorCopy(p->winding->points[j], point->position);
2342 PlaneClassify(&portal->plane);
2344 // link into leaf's portal chain
2345 portal->next = portal->here->portals;
2346 portal->here->portals = portal;
2348 // advance to next portal
2351 // then make the front to back portal(backward portal)
2352 portal->points = point;
2353 portal->numpoints = p->winding->numpoints;
2354 portal->plane.dist = -p->plane.dist;
2355 VectorNegate(p->plane.normal, portal->plane.normal);
2356 portal->here = (mleaf_t *)p->nodes[0];
2357 portal->past = (mleaf_t *)p->nodes[1];
2359 for (j = portal->numpoints - 1;j >= 0;j--)
2361 VectorCopy(p->winding->points[j], point->position);
2364 PlaneClassify(&portal->plane);
2366 // link into leaf's portal chain
2367 portal->next = portal->here->portals;
2368 portal->here->portals = portal;
2370 // advance to next portal
2373 Winding_Free(p->winding);
2385 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2388 Host_Error("AddPortalToNodes: NULL front node");
2390 Host_Error("AddPortalToNodes: NULL back node");
2391 if (p->nodes[0] || p->nodes[1])
2392 Host_Error("AddPortalToNodes: already included");
2393 // 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
2395 p->nodes[0] = front;
2396 p->next[0] = (portal_t *)front->portals;
2397 front->portals = (mportal_t *)p;
2400 p->next[1] = (portal_t *)back->portals;
2401 back->portals = (mportal_t *)p;
2406 RemovePortalFromNode
2409 static void RemovePortalFromNodes(portal_t *portal)
2413 void **portalpointer;
2415 for (i = 0;i < 2;i++)
2417 node = portal->nodes[i];
2419 portalpointer = (void **) &node->portals;
2424 Host_Error("RemovePortalFromNodes: portal not in leaf");
2428 if (portal->nodes[0] == node)
2430 *portalpointer = portal->next[0];
2431 portal->nodes[0] = NULL;
2433 else if (portal->nodes[1] == node)
2435 *portalpointer = portal->next[1];
2436 portal->nodes[1] = NULL;
2439 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2443 if (t->nodes[0] == node)
2444 portalpointer = (void **) &t->next[0];
2445 else if (t->nodes[1] == node)
2446 portalpointer = (void **) &t->next[1];
2448 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2453 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2456 mnode_t *front, *back, *other_node;
2457 mplane_t clipplane, *plane;
2458 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2459 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2461 // if a leaf, we're done
2465 plane = node->plane;
2467 front = node->children[0];
2468 back = node->children[1];
2470 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2472 // create the new portal by generating a polygon for the node plane,
2473 // and clipping it by all of the other portals(which came from nodes above this one)
2474 nodeportal = AllocPortal();
2475 nodeportal->plane = *plane;
2477 nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2478 side = 0; // shut up compiler warning
2479 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2481 clipplane = portal->plane;
2482 if (portal->nodes[0] == portal->nodes[1])
2483 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2484 if (portal->nodes[0] == node)
2486 else if (portal->nodes[1] == node)
2488 clipplane.dist = -clipplane.dist;
2489 VectorNegate(clipplane.normal, clipplane.normal);
2493 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2495 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2496 if (!nodeportalwinding)
2498 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2503 if (nodeportalwinding)
2505 // if the plane was not clipped on all sides, there was an error
2506 nodeportal->winding = nodeportalwinding;
2507 AddPortalToNodes(nodeportal, front, back);
2510 // split the portals of this node along this node's plane and assign them to the children of this node
2511 // (migrating the portals downward through the tree)
2512 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2514 if (portal->nodes[0] == portal->nodes[1])
2515 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2516 if (portal->nodes[0] == node)
2518 else if (portal->nodes[1] == node)
2521 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2522 nextportal = portal->next[side];
2524 other_node = portal->nodes[!side];
2525 RemovePortalFromNodes(portal);
2527 // cut the portal into two portals, one on each side of the node plane
2528 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2533 AddPortalToNodes(portal, back, other_node);
2535 AddPortalToNodes(portal, other_node, back);
2541 AddPortalToNodes(portal, front, other_node);
2543 AddPortalToNodes(portal, other_node, front);
2547 // the winding is split
2548 splitportal = AllocPortal();
2549 temp = splitportal->chain;
2550 *splitportal = *portal;
2551 splitportal->chain = temp;
2552 splitportal->winding = backwinding;
2553 Winding_Free(portal->winding);
2554 portal->winding = frontwinding;
2558 AddPortalToNodes(portal, front, other_node);
2559 AddPortalToNodes(splitportal, back, other_node);
2563 AddPortalToNodes(portal, other_node, front);
2564 AddPortalToNodes(splitportal, other_node, back);
2568 Mod_Q1BSP_RecursiveNodePortals(front);
2569 Mod_Q1BSP_RecursiveNodePortals(back);
2572 static void Mod_Q1BSP_MakePortals(void)
2575 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2576 Mod_Q1BSP_FinalizePortals();
2579 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2582 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2583 msurface_t *surf, *s;
2584 float *v0, *v1, *v2, *v3;
2585 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2586 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2587 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2589 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)
2591 if (surf->neighborsurfaces[vertnum])
2593 surf->neighborsurfaces[vertnum] = NULL;
2594 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2596 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2597 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2598 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2601 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2602 if (s->neighborsurfaces[vnum] == surf)
2604 if (vnum < s->poly_numverts)
2606 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)
2608 if (s->neighborsurfaces[vnum] == NULL
2609 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2610 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2612 surf->neighborsurfaces[vertnum] = s;
2613 s->neighborsurfaces[vnum] = surf;
2617 if (vnum < s->poly_numverts)
2625 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2627 int i, j, stylecounts[256], totalcount, remapstyles[256];
2629 memset(stylecounts, 0, sizeof(stylecounts));
2630 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2632 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2633 for (j = 0;j < MAXLIGHTMAPS;j++)
2634 stylecounts[surf->styles[j]]++;
2637 model->brushq1.light_styles = 0;
2638 for (i = 0;i < 255;i++)
2642 remapstyles[i] = model->brushq1.light_styles++;
2643 totalcount += stylecounts[i] + 1;
2648 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2649 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2650 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2651 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2652 model->brushq1.light_styles = 0;
2653 for (i = 0;i < 255;i++)
2655 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2657 for (i = 0;i < model->brushq1.light_styles;i++)
2659 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2660 j += stylecounts[model->brushq1.light_style[i]] + 1;
2662 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2664 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2665 for (j = 0;j < MAXLIGHTMAPS;j++)
2666 if (surf->styles[j] != 255)
2667 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2670 for (i = 0;i < model->brushq1.light_styles;i++)
2672 *model->brushq1.light_styleupdatechains[i] = NULL;
2673 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2674 j += stylecounts[model->brushq1.light_style[i]] + 1;
2678 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2681 for (i = 0;i < model->brushq1.numtextures;i++)
2682 model->brushq1.pvstexturechainslength[i] = 0;
2683 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2685 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2687 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2688 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2691 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2693 if (model->brushq1.pvstexturechainslength[i])
2695 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2696 j += model->brushq1.pvstexturechainslength[i] + 1;
2699 model->brushq1.pvstexturechains[i] = NULL;
2701 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2702 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2703 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2704 for (i = 0;i < model->brushq1.numtextures;i++)
2706 if (model->brushq1.pvstexturechainslength[i])
2708 *model->brushq1.pvstexturechains[i] = NULL;
2709 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2714 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2719 while (node->contents >= 0)
2721 d = PlaneDiff(org, node->plane);
2723 node = node->children[0];
2724 else if (d < -radius)
2725 node = node->children[1];
2728 // go down both sides
2729 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2730 node = node->children[1];
2734 // if this is a leaf, accumulate the pvs bits
2735 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2736 for (i = 0;i < pvsbytes;i++)
2737 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2740 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2741 //of the given point.
2742 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2744 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2745 bytes = min(bytes, pvsbufferlength);
2746 memset(pvsbuffer, 0, bytes);
2747 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2751 //Returns PVS data for a given point
2752 //(note: always returns valid data, never NULL)
2753 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2756 Mod_CheckLoaded(model);
2757 // LordHavoc: modified to start at first clip node,
2758 // in other words: first node of the (sub)model
2759 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2760 while (node->contents == 0)
2761 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2762 return ((mleaf_t *)node)->pvsdata;
2765 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2770 VectorSubtract(inmaxs, inmins, size);
2771 if (cmodel->brush.ishlbsp)
2774 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2775 else if (size[0] <= 32)
2777 if (size[2] < 54) // pick the nearest of 36 or 72
2778 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2780 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2783 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2788 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2789 else if (size[0] <= 32)
2790 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2792 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2794 VectorCopy(inmins, outmins);
2795 VectorAdd(inmins, hull->clip_size, outmaxs);
2798 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2799 extern void R_Model_Brush_Draw(entity_render_t *ent);
2800 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2801 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);
2802 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2807 mempool_t *mainmempool;
2809 model_t *originalloadmodel;
2810 float dist, modelyawradius, modelradius, *vec;
2813 mod->type = mod_brush;
2815 header = (dheader_t *)buffer;
2817 i = LittleLong(header->version);
2818 if (i != BSPVERSION && i != 30)
2819 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2820 mod->brush.ishlbsp = i == 30;
2822 mod->soundfromcenter = true;
2823 mod->TraceBox = Mod_Q1BSP_TraceBox;
2824 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2825 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2826 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2827 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2828 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2829 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2830 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2831 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2832 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2833 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2834 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2836 if (loadmodel->isworldmodel)
2838 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2839 // until we get a texture for it...
2843 // swap all the lumps
2844 mod_base = (qbyte *)header;
2846 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2847 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2851 // store which lightmap format to use
2852 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2854 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2855 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2856 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2857 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2858 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2859 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2860 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2861 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2862 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2863 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2864 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2865 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2866 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2867 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2868 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2870 if (mod->brushq1.data_compressedpvs)
2871 Mem_Free(mod->brushq1.data_compressedpvs);
2872 mod->brushq1.data_compressedpvs = NULL;
2873 mod->brushq1.num_compressedpvs = 0;
2875 Mod_Q1BSP_MakeHull0();
2876 Mod_Q1BSP_MakePortals();
2878 if (developer.integer)
2879 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);
2881 mod->numframes = 2; // regular and alternate animation
2883 mainmempool = mod->mempool;
2884 loadname = mod->name;
2886 Mod_Q1BSP_LoadLightList();
2887 originalloadmodel = loadmodel;
2890 // set up the submodels(FIXME: this is confusing)
2892 for (i = 0;i < mod->brush.numsubmodels;i++)
2894 bm = &mod->brushq1.submodels[i];
2896 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2897 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2899 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2900 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2903 mod->brushq1.firstmodelsurface = bm->firstface;
2904 mod->brushq1.nummodelsurfaces = bm->numfaces;
2906 // this gets altered below if sky is used
2907 mod->DrawSky = NULL;
2908 mod->Draw = R_Model_Brush_Draw;
2909 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2910 mod->DrawLight = R_Model_Brush_DrawLight;
2911 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2912 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2913 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2914 Mod_Q1BSP_BuildPVSTextureChains(mod);
2915 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2916 if (mod->brushq1.nummodelsurfaces)
2918 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2919 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2920 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2923 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2925 // we only need to have a drawsky function if it is used(usually only on world model)
2926 if (surf->texinfo->texture->shader == &Cshader_sky)
2927 mod->DrawSky = R_Model_Brush_DrawSky;
2928 // LordHavoc: submodels always clip, even if water
2929 if (mod->brush.numsubmodels - 1)
2930 surf->flags |= SURF_SOLIDCLIP;
2931 // calculate bounding shapes
2932 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
2934 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2935 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2936 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2937 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2938 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2939 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2940 dist = vec[0]*vec[0]+vec[1]*vec[1];
2941 if (modelyawradius < dist)
2942 modelyawradius = dist;
2943 dist += vec[2]*vec[2];
2944 if (modelradius < dist)
2948 modelyawradius = sqrt(modelyawradius);
2949 modelradius = sqrt(modelradius);
2950 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2951 mod->yawmins[2] = mod->normalmins[2];
2952 mod->yawmaxs[2] = mod->normalmaxs[2];
2953 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2954 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2955 mod->radius = modelradius;
2956 mod->radius2 = modelradius * modelradius;
2960 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2961 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2963 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2965 mod->brushq1.visleafs = bm->visleafs;
2967 // LordHavoc: only register submodels if it is the world
2968 // (prevents bsp models from replacing world submodels)
2969 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2972 // duplicate the basic information
2973 sprintf(name, "*%i", i+1);
2974 loadmodel = Mod_FindName(name);
2976 strcpy(loadmodel->name, name);
2977 // textures and memory belong to the main model
2978 loadmodel->texturepool = NULL;
2979 loadmodel->mempool = NULL;
2984 loadmodel = originalloadmodel;
2985 //Mod_Q1BSP_ProcessLightList();
2988 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2992 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2999 in = (void *)(mod_base + l->fileofs);
3000 if (l->filelen % sizeof(*in))
3001 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3002 count = l->filelen / sizeof(*in);
3003 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3006 loadmodel->num = count;
3008 for (i = 0;i < count;i++, in++, out++)
3014 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3021 in = (void *)(mod_base + l->fileofs);
3022 if (l->filelen % sizeof(*in))
3023 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3024 count = l->filelen / sizeof(*in);
3025 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3028 loadmodel->num = count;
3030 for (i = 0;i < count;i++, in++, out++)
3036 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3043 in = (void *)(mod_base + l->fileofs);
3044 if (l->filelen % sizeof(*in))
3045 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3046 count = l->filelen / sizeof(*in);
3047 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3050 loadmodel->num = count;
3052 for (i = 0;i < count;i++, in++, out++)
3058 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3065 in = (void *)(mod_base + l->fileofs);
3066 if (l->filelen % sizeof(*in))
3067 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3068 count = l->filelen / sizeof(*in);
3069 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3072 loadmodel->num = count;
3074 for (i = 0;i < count;i++, in++, out++)
3080 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3087 in = (void *)(mod_base + l->fileofs);
3088 if (l->filelen % sizeof(*in))
3089 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3090 count = l->filelen / sizeof(*in);
3091 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3094 loadmodel->num = count;
3096 for (i = 0;i < count;i++, in++, out++)
3102 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3109 in = (void *)(mod_base + l->fileofs);
3110 if (l->filelen % sizeof(*in))
3111 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3112 count = l->filelen / sizeof(*in);
3113 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3116 loadmodel->num = count;
3118 for (i = 0;i < count;i++, in++, out++)
3124 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3131 in = (void *)(mod_base + l->fileofs);
3132 if (l->filelen % sizeof(*in))
3133 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3134 count = l->filelen / sizeof(*in);
3135 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3138 loadmodel->num = count;
3140 for (i = 0;i < count;i++, in++, out++)
3146 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3153 in = (void *)(mod_base + l->fileofs);
3154 if (l->filelen % sizeof(*in))
3155 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3156 count = l->filelen / sizeof(*in);
3157 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3160 loadmodel->num = count;
3162 for (i = 0;i < count;i++, in++, out++)
3168 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3175 in = (void *)(mod_base + l->fileofs);
3176 if (l->filelen % sizeof(*in))
3177 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3178 count = l->filelen / sizeof(*in);
3179 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3182 loadmodel->num = count;
3184 for (i = 0;i < count;i++, in++, out++)
3190 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3197 in = (void *)(mod_base + l->fileofs);
3198 if (l->filelen % sizeof(*in))
3199 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3200 count = l->filelen / sizeof(*in);
3201 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3204 loadmodel->num = count;
3206 for (i = 0;i < count;i++, in++, out++)
3212 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3219 in = (void *)(mod_base + l->fileofs);
3220 if (l->filelen % sizeof(*in))
3221 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3222 count = l->filelen / sizeof(*in);
3223 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3226 loadmodel->num = count;
3228 for (i = 0;i < count;i++, in++, out++)
3234 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3241 in = (void *)(mod_base + l->fileofs);
3242 if (l->filelen % sizeof(*in))
3243 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3244 count = l->filelen / sizeof(*in);
3245 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3248 loadmodel->num = count;
3250 for (i = 0;i < count;i++, in++, out++)
3256 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3263 in = (void *)(mod_base + l->fileofs);
3264 if (l->filelen % sizeof(*in))
3265 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3266 count = l->filelen / sizeof(*in);
3267 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3270 loadmodel->num = count;
3272 for (i = 0;i < count;i++, in++, out++)
3278 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3285 in = (void *)(mod_base + l->fileofs);
3286 if (l->filelen % sizeof(*in))
3287 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3288 count = l->filelen / sizeof(*in);
3289 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3292 loadmodel->num = count;
3294 for (i = 0;i < count;i++, in++, out++)
3300 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3307 in = (void *)(mod_base + l->fileofs);
3308 if (l->filelen % sizeof(*in))
3309 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3310 count = l->filelen / sizeof(*in);
3311 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3314 loadmodel->num = count;
3316 for (i = 0;i < count;i++, in++, out++)
3322 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3329 in = (void *)(mod_base + l->fileofs);
3330 if (l->filelen % sizeof(*in))
3331 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3332 count = l->filelen / sizeof(*in);
3333 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3336 loadmodel->num = count;
3338 for (i = 0;i < count;i++, in++, out++)
3344 static void Mod_Q2BSP_LoadModels(lump_t *l)
3351 in = (void *)(mod_base + l->fileofs);
3352 if (l->filelen % sizeof(*in))
3353 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3354 count = l->filelen / sizeof(*in);
3355 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3358 loadmodel->num = count;
3360 for (i = 0;i < count;i++, in++, out++)
3366 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3369 q2dheader_t *header;
3371 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3373 mod->type = mod_brushq2;
3375 header = (q2dheader_t *)buffer;
3377 i = LittleLong(header->version);
3378 if (i != Q2BSPVERSION)
3379 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3380 mod->brush.ishlbsp = false;
3381 if (loadmodel->isworldmodel)
3383 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3384 // until we get a texture for it...
3388 mod_base = (qbyte *)header;
3390 // swap all the lumps
3391 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3392 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3394 // store which lightmap format to use
3395 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3397 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3398 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3399 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3400 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3401 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3402 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3403 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3404 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3405 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3406 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3407 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3408 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3409 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3410 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3411 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3412 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3413 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3414 // LordHavoc: must go last because this makes the submodels
3415 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3418 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3419 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3421 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3424 char key[128], value[4096];
3426 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3427 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3428 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3431 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3432 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3433 data = loadmodel->brush.entities;
3434 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3435 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3439 if (!COM_ParseToken(&data, false))
3441 if (com_token[0] == '}')
3442 break; // end of worldspawn
3443 if (com_token[0] == '_')
3444 strcpy(key, com_token + 1);
3446 strcpy(key, com_token);
3447 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3448 key[strlen(key)-1] = 0;
3449 if (!COM_ParseToken(&data, false))
3451 strcpy(value, com_token);
3452 if (!strcmp("gridsize", key))
3454 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3455 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3461 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3471 char shadername[Q3PATHLENGTH];
3473 in = (void *)(mod_base + l->fileofs);
3474 if (l->filelen % sizeof(*in))
3475 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3476 count = l->filelen / sizeof(*in);
3477 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3479 loadmodel->brushq3.data_textures = out;
3480 loadmodel->brushq3.num_textures = count;
3482 for (i = 0;i < count;i++, in++, out++)
3485 strlcpy (out->name, in->name, sizeof (out->name));
3486 out->surfaceflags = LittleLong(in->surfaceflags);
3487 out->nativecontents = LittleLong(in->contents);
3488 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3489 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3490 out->surfaceparms = -1;
3493 // do a quick parse of shader files to get surfaceparms
3494 if ((search = FS_Search("scripts/*.shader", true, false)))
3496 for (i = 0;i < search->numfilenames;i++)
3498 if ((f = FS_LoadFile(search->filenames[i], false)))
3501 while (COM_ParseToken(&text, false))
3503 snprintf(shadername, sizeof(shadername), "%s", com_token);
3505 if (COM_ParseToken(&text, false) && !strcmp(com_token, "{"))
3507 while (COM_ParseToken(&text, false))
3509 if (!strcmp(com_token, "}"))
3511 else if (!strcmp(com_token, "{"))
3513 while (COM_ParseToken(&text, false))
3515 if (!strcmp(com_token, "}"))
3519 else if (!strcmp(com_token, "surfaceparm"))
3521 if (COM_ParseToken(&text, true) && strcmp(com_token, "\n"))
3523 if (!strcmp(com_token, "alphashadow"))
3524 flags |= Q3SURFACEPARM_ALPHASHADOW;
3525 else if (!strcmp(com_token, "areaportal"))
3526 flags |= Q3SURFACEPARM_AREAPORTAL;
3527 else if (!strcmp(com_token, "clusterportal"))
3528 flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3529 else if (!strcmp(com_token, "detail"))
3530 flags |= Q3SURFACEPARM_DETAIL;
3531 else if (!strcmp(com_token, "donotenter"))
3532 flags |= Q3SURFACEPARM_DONOTENTER;
3533 else if (!strcmp(com_token, "fog"))
3534 flags |= Q3SURFACEPARM_FOG;
3535 else if (!strcmp(com_token, "lava"))
3536 flags |= Q3SURFACEPARM_LAVA;
3537 else if (!strcmp(com_token, "lightfilter"))
3538 flags |= Q3SURFACEPARM_LIGHTFILTER;
3539 else if (!strcmp(com_token, "metalsteps"))
3540 flags |= Q3SURFACEPARM_METALSTEPS;
3541 else if (!strcmp(com_token, "nodamage"))
3542 flags |= Q3SURFACEPARM_NODAMAGE;
3543 else if (!strcmp(com_token, "nodlight"))
3544 flags |= Q3SURFACEPARM_NODLIGHT;
3545 else if (!strcmp(com_token, "nodraw"))
3546 flags |= Q3SURFACEPARM_NODRAW;
3547 else if (!strcmp(com_token, "nodrop"))
3548 flags |= Q3SURFACEPARM_NODROP;
3549 else if (!strcmp(com_token, "noimpact"))
3550 flags |= Q3SURFACEPARM_NOIMPACT;
3551 else if (!strcmp(com_token, "nolightmap"))
3552 flags |= Q3SURFACEPARM_NOLIGHTMAP;
3553 else if (!strcmp(com_token, "nomarks"))
3554 flags |= Q3SURFACEPARM_NOMARKS;
3555 else if (!strcmp(com_token, "nomipmaps"))
3556 flags |= Q3SURFACEPARM_NOMIPMAPS;
3557 else if (!strcmp(com_token, "nonsolid"))
3558 flags |= Q3SURFACEPARM_NONSOLID;
3559 else if (!strcmp(com_token, "origin"))
3560 flags |= Q3SURFACEPARM_ORIGIN;
3561 else if (!strcmp(com_token, "playerclip"))
3562 flags |= Q3SURFACEPARM_PLAYERCLIP;
3563 else if (!strcmp(com_token, "sky"))
3564 flags |= Q3SURFACEPARM_SKY;
3565 else if (!strcmp(com_token, "slick"))
3566 flags |= Q3SURFACEPARM_SLICK;
3567 else if (!strcmp(com_token, "slime"))
3568 flags |= Q3SURFACEPARM_SLIME;
3569 else if (!strcmp(com_token, "structural"))
3570 flags |= Q3SURFACEPARM_STRUCTURAL;
3571 else if (!strcmp(com_token, "trans"))
3572 flags |= Q3SURFACEPARM_TRANS;
3573 else if (!strcmp(com_token, "water"))
3574 flags |= Q3SURFACEPARM_WATER;
3576 Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
3577 if (!COM_ParseToken(&text, true) || strcmp(com_token, "\n"))
3579 Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
3585 Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
3591 // look for linebreak or }
3592 while(COM_ParseToken(&text, true) && strcmp(com_token, "\n") && strcmp(com_token, "}"));
3593 // break out to top level if it was }
3594 if (!strcmp(com_token, "}"))
3598 // add shader to list (shadername and flags)
3599 // actually here we just poke into the texture settings
3600 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3601 if (!strcmp(out->name, shadername))
3602 out->surfaceparms = flags;
3606 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3617 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3619 if (out->surfaceparms == -1)
3622 Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3623 out->surfaceparms = 0;
3624 // these are defaults
3625 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3626 || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3627 out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3628 if (!strncmp(out->name, "textures/skies/", 15))
3629 out->surfaceparms |= Q3SURFACEPARM_SKY;
3630 if (R_TextureHasAlpha(out->skin.base))
3631 out->surfaceparms |= Q3SURFACEPARM_TRANS;
3634 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3637 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3643 in = (void *)(mod_base + l->fileofs);
3644 if (l->filelen % sizeof(*in))
3645 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3646 count = l->filelen / sizeof(*in);
3647 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3649 loadmodel->brushq3.data_planes = out;
3650 loadmodel->brushq3.num_planes = count;
3652 for (i = 0;i < count;i++, in++, out++)
3654 out->normal[0] = LittleLong(in->normal[0]);
3655 out->normal[1] = LittleLong(in->normal[1]);
3656 out->normal[2] = LittleLong(in->normal[2]);
3657 out->dist = LittleLong(in->dist);
3662 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3665 q3mbrushside_t *out;
3668 in = (void *)(mod_base + l->fileofs);
3669 if (l->filelen % sizeof(*in))
3670 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3671 count = l->filelen / sizeof(*in);
3672 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3674 loadmodel->brushq3.data_brushsides = out;
3675 loadmodel->brushq3.num_brushsides = count;
3677 for (i = 0;i < count;i++, in++, out++)
3679 n = LittleLong(in->planeindex);
3680 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3681 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3682 out->plane = loadmodel->brushq3.data_planes + n;
3683 n = LittleLong(in->textureindex);
3684 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3685 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3686 out->texture = loadmodel->brushq3.data_textures + n;
3690 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3694 int i, j, n, c, count, maxplanes;
3696 winding_t *temp1, *temp2;
3698 in = (void *)(mod_base + l->fileofs);
3699 if (l->filelen % sizeof(*in))
3700 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3701 count = l->filelen / sizeof(*in);
3702 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3704 loadmodel->brushq3.data_brushes = out;
3705 loadmodel->brushq3.num_brushes = count;
3707 temp1 = Winding_New(64);
3708 temp2 = Winding_New(64);
3713 for (i = 0;i < count;i++, in++, out++)
3715 n = LittleLong(in->firstbrushside);
3716 c = LittleLong(in->numbrushsides);
3717 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3718 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3719 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3720 out->numbrushsides = c;
3721 n = LittleLong(in->textureindex);
3722 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3723 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3724 out->texture = loadmodel->brushq3.data_textures + n;
3726 // make a list of mplane_t structs to construct a colbrush from
3727 if (maxplanes < out->numbrushsides)
3729 maxplanes = out->numbrushsides;
3732 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3734 for (j = 0;j < out->numbrushsides;j++)
3736 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3737 planes[j].dist = out->firstbrushside[j].plane->dist;
3739 // make the colbrush from the planes
3740 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
3744 Winding_Free(temp1);
3745 Winding_Free(temp2);
3748 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3754 in = (void *)(mod_base + l->fileofs);
3755 if (l->filelen % sizeof(*in))
3756 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3757 count = l->filelen / sizeof(*in);
3758 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3760 loadmodel->brushq3.data_effects = out;
3761 loadmodel->brushq3.num_effects = count;
3763 for (i = 0;i < count;i++, in++, out++)
3765 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3766 n = LittleLong(in->brushindex);
3767 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3768 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3769 out->brush = loadmodel->brushq3.data_brushes + n;
3770 out->unknown = LittleLong(in->unknown);
3774 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3779 in = (void *)(mod_base + l->fileofs);
3780 if (l->filelen % sizeof(*in))
3781 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3782 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3783 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3784 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3785 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3786 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3787 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3788 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3789 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3791 for (i = 0;i < count;i++, in++)
3793 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3794 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3795 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3796 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3797 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3798 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3799 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3800 // svector/tvector are calculated later in face loading
3801 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3802 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3803 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3804 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3805 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3806 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3807 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3808 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3809 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3810 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3811 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3812 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3813 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3817 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3823 in = (void *)(mod_base + l->fileofs);
3824 if (l->filelen % sizeof(int[3]))
3825 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3826 count = l->filelen / sizeof(*in);
3827 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3829 loadmodel->brushq3.num_triangles = count / 3;
3830 loadmodel->brushq3.data_element3i = out;
3831 loadmodel->brushq3.data_neighbor3i = out + count;
3833 for (i = 0;i < count;i++, in++, out++)
3835 *out = LittleLong(*in);
3836 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3838 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3844 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3852 in = (void *)(mod_base + l->fileofs);
3853 if (l->filelen % sizeof(*in))
3854 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3855 count = l->filelen / sizeof(*in);
3856 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3858 loadmodel->brushq3.data_lightmaps = out;
3859 loadmodel->brushq3.num_lightmaps = count;
3861 for (i = 0;i < count;i++, in++, out++)
3862 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3865 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3869 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3870 //int *originalelement3i;
3871 //int *originalneighbor3i;
3872 float *originalvertex3f;
3873 //float *originalsvector3f;
3874 //float *originaltvector3f;
3875 //float *originalnormal3f;
3876 float *originalcolor4f;
3877 float *originaltexcoordtexture2f;
3878 float *originaltexcoordlightmap2f;
3881 in = (void *)(mod_base + l->fileofs);
3882 if (l->filelen % sizeof(*in))
3883 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3884 count = l->filelen / sizeof(*in);
3885 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3887 loadmodel->brushq3.data_faces = out;
3888 loadmodel->brushq3.num_faces = count;
3890 for (i = 0;i < count;i++, in++, out++)
3892 // check face type first
3893 out->type = LittleLong(in->type);
3894 if (out->type != Q3FACETYPE_POLYGON
3895 && out->type != Q3FACETYPE_PATCH
3896 && out->type != Q3FACETYPE_MESH
3897 && out->type != Q3FACETYPE_FLARE)
3899 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3900 out->num_vertices = 0;
3901 out->num_triangles = 0;
3902 out->type = 0; // error
3906 n = LittleLong(in->textureindex);
3907 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3909 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3910 out->num_vertices = 0;
3911 out->num_triangles = 0;
3912 out->type = 0; // error
3916 out->texture = loadmodel->brushq3.data_textures + n;
3917 n = LittleLong(in->effectindex);
3918 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3920 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3926 out->effect = loadmodel->brushq3.data_effects + n;
3927 n = LittleLong(in->lightmapindex);
3928 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3930 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3934 out->lightmaptexture = NULL;
3936 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3938 out->firstvertex = LittleLong(in->firstvertex);
3939 out->num_vertices = LittleLong(in->numvertices);
3940 out->firstelement = LittleLong(in->firstelement);
3941 out->num_triangles = LittleLong(in->numelements) / 3;
3942 if (out->num_triangles * 3 != LittleLong(in->numelements))
3944 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));
3945 out->num_vertices = 0;
3946 out->num_triangles = 0;
3947 out->type = 0; // error
3950 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3952 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);
3953 out->num_vertices = 0;
3954 out->num_triangles = 0;
3955 out->type = 0; // error
3958 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3960 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);
3961 out->num_vertices = 0;
3962 out->num_triangles = 0;
3963 out->type = 0; // error
3968 case Q3FACETYPE_POLYGON:
3969 case Q3FACETYPE_MESH:
3970 // no processing necessary
3971 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3972 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3973 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3974 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3975 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3976 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3977 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3978 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3979 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3981 case Q3FACETYPE_PATCH:
3982 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3983 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3984 if (patchsize[0] < 1 || patchsize[1] < 1)
3986 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3987 out->num_vertices = 0;
3988 out->num_triangles = 0;
3989 out->type = 0; // error
3992 // convert patch to Q3FACETYPE_MESH
3993 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3994 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3995 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3996 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3997 finalvertices = finalwidth * finalheight;
3998 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3999 originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4000 //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4001 //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4002 //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4003 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4004 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4005 originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4006 //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
4007 //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4009 originalvertex3f = out->data_vertex3f;
4010 //originalsvector3f = out->data_svector3f;
4011 //originaltvector3f = out->data_tvector3f;
4012 //originalnormal3f = out->data_normal3f;
4013 originalcolor4f = out->data_color4f;
4014 originaltexcoordtexture2f = out->data_texcoordtexture2f;
4015 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
4016 //originalelement3i = out->data_element3i;
4017 //originalneighbor3i = out->data_neighbor3i;
4019 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
4020 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
4021 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
4022 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
4023 out->data_color4f = out->data_normal3f + finalvertices * 3;
4024 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
4025 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
4026 out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
4027 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
4028 out->type = Q3FACETYPE_MESH;
4029 out->firstvertex = -1;
4030 out->num_vertices = finalvertices;
4031 out->firstelement = -1;
4032 out->num_triangles = finaltriangles;
4033 // generate geometry
4034 // (note: normals are skipped because they get recalculated)
4035 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
4036 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
4037 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
4038 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
4039 // generate elements
4040 e = out->data_element3i;
4041 for (y = 0;y < finalheight - 1;y++)
4043 row0 = (y + 0) * finalwidth;
4044 row1 = (y + 1) * finalwidth;
4045 for (x = 0;x < finalwidth - 1;x++)
4057 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
4058 if (developer.integer)
4060 if (out->num_triangles < finaltriangles)
4061 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);
4063 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);
4065 // q3map does not put in collision brushes for curves... ugh
4066 out->collisions = true;
4068 case Q3FACETYPE_FLARE:
4069 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4071 out->num_vertices = 0;
4072 out->num_triangles = 0;
4076 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4077 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4079 if (invalidelements)
4081 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);
4082 for (j = 0;j < out->num_triangles * 3;j++)
4084 Con_Printf(" %i", out->data_element3i[j]);
4085 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4086 out->data_element3i[j] = 0;
4090 // for shadow volumes
4091 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
4092 // for per pixel lighting
4093 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);
4094 // calculate a bounding box
4095 VectorClear(out->mins);
4096 VectorClear(out->maxs);
4097 if (out->num_vertices)
4099 VectorCopy(out->data_vertex3f, out->mins);
4100 VectorCopy(out->data_vertex3f, out->maxs);
4101 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
4103 out->mins[0] = min(out->mins[0], v[0]);
4104 out->maxs[0] = max(out->maxs[0], v[0]);
4105 out->mins[1] = min(out->mins[1], v[1]);
4106 out->maxs[1] = max(out->maxs[1], v[1]);
4107 out->mins[2] = min(out->mins[2], v[2]);
4108 out->maxs[2] = max(out->maxs[2], v[2]);
4110 out->mins[0] -= 1.0f;
4111 out->mins[1] -= 1.0f;
4112 out->mins[2] -= 1.0f;
4113 out->maxs[0] += 1.0f;
4114 out->maxs[1] += 1.0f;
4115 out->maxs[2] += 1.0f;
4119 // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
4122 int totalverts, totaltris;
4123 int originalnum_vertices;
4124 float *originaldata_vertex3f;
4125 float *originaldata_texcoordtexture2f;
4126 float *originaldata_texcoordlightmap2f;
4127 float *originaldata_svector3f;
4128 float *originaldata_tvector3f;
4129 float *originaldata_normal3f;
4130 float *originaldata_color4f;
4131 int originalnum_triangles;
4132 int *originaldata_element3i;
4133 int *originaldata_neighbor3i;
4137 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4141 totalverts += out->num_vertices;
4142 totaltris += out->num_triangles;
4145 originalnum_vertices = loadmodel->brushq3.num_vertices;
4146 originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
4147 originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
4148 originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
4149 originaldata_svector3f = loadmodel->brushq3.data_svector3f;
4150 originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
4151 originaldata_normal3f = loadmodel->brushq3.data_normal3f;
4152 originaldata_color4f = loadmodel->brushq3.data_color4f;
4153 originalnum_triangles = loadmodel->brushq3.num_triangles;
4154 originaldata_element3i = loadmodel->brushq3.data_element3i;
4155 originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
4156 loadmodel->brushq3.num_vertices = totalverts;
4157 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
4158 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
4159 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
4160 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
4161 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
4162 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
4163 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
4164 loadmodel->brushq3.num_triangles = totaltris;
4165 loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
4166 loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
4169 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4173 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
4174 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
4175 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
4176 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
4177 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
4178 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
4179 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
4180 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
4181 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
4182 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
4183 if (out->firstvertex == -1)
4184 Mem_Free(out->data_vertex3f);
4185 if (out->firstelement == -1)
4186 Mem_Free(out->data_element3i);
4187 out->firstvertex = totalverts;
4188 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4189 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4190 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4191 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4192 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4193 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4194 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4195 out->firstelement = totaltris * 3;
4196 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4197 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4198 //for (j = 0;j < out->numtriangles * 3;j++)
4199 // out->data_element3i[j] += totalverts - out->firstvertex;
4200 totalverts += out->num_vertices;
4201 totaltris += out->num_triangles;
4203 Mem_Free(originaldata_vertex3f);
4204 Mem_Free(originaldata_element3i);
4209 static void Mod_Q3BSP_LoadModels(lump_t *l)
4213 int i, j, n, c, count;
4215 in = (void *)(mod_base + l->fileofs);
4216 if (l->filelen % sizeof(*in))
4217 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4218 count = l->filelen / sizeof(*in);
4219 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4221 loadmodel->brushq3.data_models = out;
4222 loadmodel->brushq3.num_models = count;
4224 for (i = 0;i < count;i++, in++, out++)
4226 for (j = 0;j < 3;j++)
4228 out->mins[j] = LittleFloat(in->mins[j]);
4229 out->maxs[j] = LittleFloat(in->maxs[j]);
4231 n = LittleLong(in->firstface);
4232 c = LittleLong(in->numfaces);
4233 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4234 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4235 out->firstface = loadmodel->brushq3.data_faces + n;
4237 n = LittleLong(in->firstbrush);
4238 c = LittleLong(in->numbrushes);
4239 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4240 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4241 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4242 out->numbrushes = c;
4246 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4252 in = (void *)(mod_base + l->fileofs);
4253 if (l->filelen % sizeof(*in))
4254 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4255 count = l->filelen / sizeof(*in);
4256 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4258 loadmodel->brushq3.data_leafbrushes = out;
4259 loadmodel->brushq3.num_leafbrushes = count;
4261 for (i = 0;i < count;i++, in++, out++)
4263 n = LittleLong(*in);
4264 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4265 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4266 *out = loadmodel->brushq3.data_brushes + n;
4270 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4276 in = (void *)(mod_base + l->fileofs);
4277 if (l->filelen % sizeof(*in))
4278 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4279 count = l->filelen / sizeof(*in);
4280 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4282 loadmodel->brushq3.data_leaffaces = out;
4283 loadmodel->brushq3.num_leaffaces = count;
4285 for (i = 0;i < count;i++, in++, out++)
4287 n = LittleLong(*in);
4288 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4289 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4290 *out = loadmodel->brushq3.data_faces + n;
4294 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4298 int i, j, n, c, count;
4300 in = (void *)(mod_base + l->fileofs);
4301 if (l->filelen % sizeof(*in))
4302 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4303 count = l->filelen / sizeof(*in);
4304 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4306 loadmodel->brushq3.data_leafs = out;
4307 loadmodel->brushq3.num_leafs = count;
4309 for (i = 0;i < count;i++, in++, out++)
4311 out->isnode = false;
4313 out->clusterindex = LittleLong(in->clusterindex);
4314 out->areaindex = LittleLong(in->areaindex);
4315 for (j = 0;j < 3;j++)
4317 // yes the mins/maxs are ints
4318 out->mins[j] = LittleLong(in->mins[j]);
4319 out->maxs[j] = LittleLong(in->maxs[j]);
4321 n = LittleLong(in->firstleafface);
4322 c = LittleLong(in->numleaffaces);
4323 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4324 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4325 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4326 out->numleaffaces = c;
4327 n = LittleLong(in->firstleafbrush);
4328 c = LittleLong(in->numleafbrushes);
4329 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4330 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4331 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4332 out->numleafbrushes = c;
4336 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4339 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4340 node->parent = parent;
4343 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4344 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4348 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4354 in = (void *)(mod_base + l->fileofs);
4355 if (l->filelen % sizeof(*in))
4356 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4357 count = l->filelen / sizeof(*in);
4358 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4360 loadmodel->brushq3.data_nodes = out;
4361 loadmodel->brushq3.num_nodes = count;
4363 for (i = 0;i < count;i++, in++, out++)
4367 n = LittleLong(in->planeindex);
4368 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4369 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4370 out->plane = loadmodel->brushq3.data_planes + n;
4371 for (j = 0;j < 2;j++)
4373 n = LittleLong(in->childrenindex[j]);
4376 if (n >= loadmodel->brushq3.num_nodes)
4377 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4378 out->children[j] = loadmodel->brushq3.data_nodes + n;
4383 if (n >= loadmodel->brushq3.num_leafs)
4384 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4385 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4388 for (j = 0;j < 3;j++)
4390 // yes the mins/maxs are ints
4391 out->mins[j] = LittleLong(in->mins[j]);
4392 out->maxs[j] = LittleLong(in->maxs[j]);
4396 // set the parent pointers
4397 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4400 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4403 q3dlightgrid_t *out;
4406 if (l->filelen == 0)
4409 in = (void *)(mod_base + l->fileofs);
4410 if (l->filelen % sizeof(*in))
4411 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4412 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4413 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4414 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4415 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4416 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4417 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4418 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4419 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4420 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4421 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4422 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4423 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4424 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4425 if (l->filelen < count * (int)sizeof(*in))
4426 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]);
4427 if (l->filelen != count * (int)sizeof(*in))
4428 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4430 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4431 loadmodel->brushq3.data_lightgrid = out;
4432 loadmodel->brushq3.num_lightgrid = count;
4434 // no swapping or validation necessary
4435 memcpy(out, in, count * (int)sizeof(*out));
4437 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]);
4438 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]);
4441 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4446 if (l->filelen == 0)
4449 in = (void *)(mod_base + l->fileofs);
4451 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4453 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4454 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4455 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4456 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4457 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4458 if (l->filelen < totalchains + (int)sizeof(*in))
4459 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);
4461 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4462 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4465 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4467 // FIXME: finish this code
4468 VectorCopy(in, out);
4471 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4473 int i, j, k, index[3];
4474 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4475 q3dlightgrid_t *a, *s;
4476 // FIXME: write this
4477 if (!model->brushq3.num_lightgrid)
4479 ambientcolor[0] = 1;
4480 ambientcolor[1] = 1;
4481 ambientcolor[2] = 1;
4484 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4485 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4486 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4487 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4488 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4489 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4490 index[0] = (int)floor(transformed[0]);
4491 index[1] = (int)floor(transformed[1]);
4492 index[2] = (int)floor(transformed[2]);
4493 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4494 // now lerp the values
4495 VectorClear(diffusenormal);
4496 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4497 for (k = 0;k < 2;k++)
4499 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4500 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4502 for (j = 0;j < 2;j++)
4504 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4505 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4507 for (i = 0;i < 2;i++)
4509 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4510 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4512 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4513 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4514 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4515 pitch = s->diffusepitch * M_PI / 128;
4516 yaw = s->diffuseyaw * M_PI / 128;
4517 sinpitch = sin(pitch);
4518 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4519 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4520 diffusenormal[2] += blend * (cos(pitch));
4521 //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)));
4525 VectorNormalize(diffusenormal);
4526 //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]);
4529 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)
4531 int i, startside, endside;
4532 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4536 if (startfrac >= trace->fraction)
4538 // note: all line fragments past first impact fraction are ignored
4539 while (node->isnode)
4541 // recurse down node sides
4542 dist1 = PlaneDiff(start, node->plane);
4543 dist2 = PlaneDiff(end, node->plane);
4544 startside = dist1 < 0;
4545 endside = dist2 < 0;
4546 if (startside == endside)
4548 // most of the time the line fragment is on one side of the plane
4549 node = node->children[startside];
4553 // line crosses node plane, split the line
4554 midfrac = dist1 / (dist1 - dist2);
4555 VectorLerp(linestart, midfrac, lineend, mid);
4556 // take the near side first
4557 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4558 if (midfrac < trace->fraction)
4559 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4564 segmentmins[0] = min(start[0], end[0]);
4565 segmentmins[1] = min(start[1], end[1]);
4566 segmentmins[2] = min(start[2], end[2]);
4567 segmentmaxs[0] = max(start[0], end[0]);
4568 segmentmaxs[1] = max(start[1], end[1]);
4569 segmentmaxs[2] = max(start[2], end[2]);
4570 leaf = (q3mleaf_t *)node;
4571 for (i = 0;i < leaf->numleafbrushes;i++)
4573 if (startfrac >= trace->fraction)
4575 brush = leaf->firstleafbrush[i]->colbrushf;
4576 if (brush && brush->markframe != markframe)
4578 brush->markframe = markframe;
4579 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4580 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4583 if (mod_q3bsp_curves_collisions.integer)
4585 for (i = 0;i < leaf->numleaffaces;i++)
4587 if (startfrac >= trace->fraction)
4589 face = leaf->firstleafface[i];
4590 if (face->collisions && face->collisionmarkframe != markframe)
4592 face->collisionmarkframe = markframe;
4593 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4594 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4600 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)
4603 float nodesegmentmins[3], nodesegmentmaxs[3];
4607 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4608 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4609 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4610 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4611 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4612 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4613 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4617 // recurse down node sides
4618 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4621 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4622 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4624 else if (sides == 2)
4625 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4627 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4629 dist = node->plane->dist - (1.0f / 8.0f);
4630 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4632 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4634 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4640 dist = node->plane->dist + (1.0f / 8.0f);
4641 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4643 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4645 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4651 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4653 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4655 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4661 leaf = (q3mleaf_t *)node;
4662 for (i = 0;i < leaf->numleafbrushes;i++)
4664 brush = leaf->firstleafbrush[i]->colbrushf;
4665 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4667 brush->markframe = markframe;
4668 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4671 if (mod_q3bsp_curves_collisions.integer)
4673 for (i = 0;i < leaf->numleaffaces;i++)
4675 face = leaf->firstleafface[i];
4676 // 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
4677 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4678 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4684 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)
4687 float segmentmins[3], segmentmaxs[3];
4688 colbrushf_t *thisbrush_start, *thisbrush_end;
4689 matrix4x4_t startmatrix, endmatrix;
4690 static int markframe = 0;
4692 memset(trace, 0, sizeof(*trace));
4693 trace->fraction = 1;
4694 trace->hitsupercontentsmask = hitsupercontentsmask;
4695 Matrix4x4_CreateIdentity(&startmatrix);
4696 Matrix4x4_CreateIdentity(&endmatrix);
4697 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4698 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4699 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4700 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4701 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4702 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4703 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4706 if (model->brushq3.submodel)
4708 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4709 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4710 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4711 if (mod_q3bsp_curves_collisions.integer)
4713 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4715 face = model->brushq3.data_thismodel->firstface + i;
4716 if (face->collisions)
4717 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4722 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4726 // box trace, performed as brush trace
4727 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4728 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4729 if (model->brushq3.submodel)
4731 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4732 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4733 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4734 if (mod_q3bsp_curves_collisions.integer)
4736 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4738 face = model->brushq3.data_thismodel->firstface + i;
4739 if (face->collisions)
4740 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4745 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4750 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)
4757 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4758 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4761 // node - recurse down the BSP tree
4762 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4765 node = node->children[0];
4768 node = node->children[1];
4770 default: // crossing
4771 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4773 node = node->children[1];
4780 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4782 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4785 //Returns PVS data for a given point
4786 //(note: can return NULL)
4787 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4790 Mod_CheckLoaded(model);
4791 node = model->brushq3.data_nodes;
4792 while (node->isnode)
4793 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4794 if (((q3mleaf_t *)node)->clusterindex >= 0)
4795 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4800 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4806 while (node->isnode)
4808 d = PlaneDiff(org, node->plane);
4810 node = node->children[0];
4811 else if (d < -radius)
4812 node = node->children[1];
4815 // go down both sides
4816 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4817 node = node->children[1];
4820 // if this is a leaf with a pvs, accumulate the pvs bits
4821 if (((q3mleaf_t *)node)->clusterindex >= 0)
4823 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4824 for (i = 0;i < pvsbytes;i++)
4825 pvsbuffer[i] |= pvs[i];
4830 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4831 //of the given point.
4832 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4834 int bytes = model->brushq3.num_pvschainlength;
4835 bytes = min(bytes, pvsbufferlength);
4836 memset(pvsbuffer, 0, bytes);
4837 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4842 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4844 int supercontents = 0;
4845 if (nativecontents & Q2CONTENTS_SOLID)
4846 supercontents |= SUPERCONTENTS_SOLID;
4847 if (nativecontents & Q2CONTENTS_WATER)
4848 supercontents |= SUPERCONTENTS_WATER;
4849 if (nativecontents & Q2CONTENTS_SLIME)
4850 supercontents |= SUPERCONTENTS_SLIME;
4851 if (nativecontents & Q2CONTENTS_LAVA)
4852 supercontents |= SUPERCONTENTS_LAVA;
4853 return supercontents;
4856 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4858 int nativecontents = 0;
4859 if (supercontents & SUPERCONTENTS_SOLID)
4860 nativecontents |= Q2CONTENTS_SOLID;
4861 if (supercontents & SUPERCONTENTS_WATER)
4862 nativecontents |= Q2CONTENTS_WATER;
4863 if (supercontents & SUPERCONTENTS_SLIME)
4864 nativecontents |= Q2CONTENTS_SLIME;
4865 if (supercontents & SUPERCONTENTS_LAVA)
4866 nativecontents |= Q2CONTENTS_LAVA;
4867 return nativecontents;
4870 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4871 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4872 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4873 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);
4874 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4877 q3dheader_t *header;
4878 float corner[3], yawradius, modelradius;
4880 mod->type = mod_brushq3;
4884 header = (q3dheader_t *)buffer;
4886 i = LittleLong(header->version);
4887 if (i != Q3BSPVERSION)
4888 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4889 if (loadmodel->isworldmodel)
4891 Cvar_SetValue("halflifebsp", false);
4892 // until we get a texture for it...
4896 mod->soundfromcenter = true;
4897 mod->TraceBox = Mod_Q3BSP_TraceBox;
4898 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4899 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4900 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4901 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4902 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4903 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4904 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4905 //mod->DrawSky = R_Q3BSP_DrawSky;
4906 mod->Draw = R_Q3BSP_Draw;
4907 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4908 mod->DrawLight = R_Q3BSP_DrawLight;
4910 mod_base = (qbyte *)header;
4912 // swap all the lumps
4913 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4914 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4916 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4917 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4918 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4919 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4920 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4921 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4922 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4923 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4924 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4925 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4926 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4927 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4928 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4929 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4930 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4931 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4932 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4933 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4935 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4942 // LordHavoc: only register submodels if it is the world
4943 // (prevents bsp models from replacing world submodels)
4944 if (!loadmodel->isworldmodel)
4946 // duplicate the basic information
4947 sprintf(name, "*%i", i);
4948 mod = Mod_FindName(name);
4950 strcpy(mod->name, name);
4951 // textures and memory belong to the main model
4952 mod->texturepool = NULL;
4953 mod->mempool = NULL;
4955 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4956 mod->brushq3.submodel = i;
4958 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4959 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4960 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4961 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4962 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4963 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4964 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4965 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4966 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4967 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4968 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4969 mod->yawmins[2] = mod->normalmins[2];
4970 mod->yawmaxs[2] = mod->normalmaxs[2];
4971 mod->radius = modelradius;
4972 mod->radius2 = modelradius * modelradius;
4974 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
4975 if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
4977 if (j < mod->brushq3.data_thismodel->numfaces)
4978 mod->DrawSky = R_Q3BSP_DrawSky;
4982 void Mod_IBSP_Load(model_t *mod, void *buffer)
4984 int i = LittleLong(((int *)buffer)[1]);
4985 if (i == Q3BSPVERSION)
4986 Mod_Q3BSP_Load(mod,buffer);
4987 else if (i == Q2BSPVERSION)
4988 Mod_Q2BSP_Load(mod,buffer);
4990 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4993 void Mod_MAP_Load(model_t *mod, void *buffer)
4995 Host_Error("Mod_MAP_Load: not yet implemented\n");