2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
38 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
39 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
41 void Mod_BrushInit(void)
43 // Cvar_RegisterVariable(&r_subdivide_size);
44 Cvar_RegisterVariable(&halflifebsp);
45 Cvar_RegisterVariable(&r_novis);
46 Cvar_RegisterVariable(&r_miplightmaps);
47 Cvar_RegisterVariable(&r_lightmaprgba);
48 Cvar_RegisterVariable(&r_nosurftextures);
49 Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
50 Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
51 Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
52 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
62 Mod_CheckLoaded(model);
64 // LordHavoc: modified to start at first clip node,
65 // in other words: first node of the (sub)model
66 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
67 while (node->contents == 0)
68 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
70 return (mleaf_t *)node;
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
77 leaf = Mod_Q1BSP_PointInLeaf(model, p);
80 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
83 memcpy(out, leaf->ambient_sound_level, i);
89 memset(out, 0, outsize);
93 static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
97 if (node->contents < 0)
100 if (node->contents == CONTENTS_SOLID)
102 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
106 // node - recurse down the BSP tree
107 switch (BoxOnPlaneSide(mins, maxs, node->plane))
110 node = node->children[0];
113 node = node->children[1];
116 if (node->children[0]->contents != CONTENTS_SOLID)
117 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
119 node = node->children[1];
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
128 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
137 return CONTENTS_EMPTY;
139 Mod_CheckLoaded(model);
141 // LordHavoc: modified to start at first clip node,
142 // in other words: first node of the (sub)model
143 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
144 while (node->contents == 0)
145 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
147 return ((mleaf_t *)node)->contents;
151 typedef struct findnonsolidlocationinfo_s
159 findnonsolidlocationinfo_t;
162 extern cvar_t samelevel;
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
166 int i, surfnum, k, *tri, *mark;
167 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
173 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
175 surf = info->model->brushq1.surfaces + *mark;
176 if (surf->flags & SURF_SOLIDCLIP)
179 VectorCopy(surf->plane->normal, surfnormal);
180 if (surf->flags & SURF_PLANEBACK)
181 VectorNegate(surfnormal, surfnormal);
183 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
185 for (k = 0;k < mesh->numtriangles;k++)
187 tri = mesh->element3i + k * 3;
188 VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
189 VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
190 VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
191 VectorSubtract(vert[1], vert[0], edge[0]);
192 VectorSubtract(vert[2], vert[1], edge[1]);
193 CrossProduct(edge[1], edge[0], facenormal);
194 if (facenormal[0] || facenormal[1] || facenormal[2])
196 VectorNormalize(facenormal);
198 if (VectorDistance(facenormal, surfnormal) > 0.01f)
199 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
201 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
202 if (f <= info->bestdist && f >= -info->bestdist)
204 VectorSubtract(vert[0], vert[2], edge[2]);
205 VectorNormalize(edge[0]);
206 VectorNormalize(edge[1]);
207 VectorNormalize(edge[2]);
208 CrossProduct(facenormal, edge[0], edgenormal[0]);
209 CrossProduct(facenormal, edge[1], edgenormal[1]);
210 CrossProduct(facenormal, edge[2], edgenormal[2]);
212 if (samelevel.integer & 1)
213 VectorNegate(edgenormal[0], edgenormal[0]);
214 if (samelevel.integer & 2)
215 VectorNegate(edgenormal[1], edgenormal[1]);
216 if (samelevel.integer & 4)
217 VectorNegate(edgenormal[2], edgenormal[2]);
218 for (i = 0;i < 3;i++)
219 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
220 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
221 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
222 Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]);
225 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
226 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
227 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
229 // we got lucky, the center is within the face
230 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
234 if (info->bestdist > dist)
236 info->bestdist = dist;
237 VectorScale(facenormal, (info->radius - -dist), info->nudge);
242 if (info->bestdist > dist)
244 info->bestdist = dist;
245 VectorScale(facenormal, (info->radius - dist), info->nudge);
251 // check which edge or vertex the center is nearest
252 for (i = 0;i < 3;i++)
254 f = DotProduct(info->center, edge[i]);
255 if (f >= DotProduct(vert[0], edge[i])
256 && f <= DotProduct(vert[1], edge[i]))
259 VectorMA(info->center, -f, edge[i], point);
260 dist = sqrt(DotProduct(point, point));
261 if (info->bestdist > dist)
263 info->bestdist = dist;
264 VectorScale(point, (info->radius / dist), info->nudge);
266 // skip both vertex checks
267 // (both are further away than this edge)
272 // not on edge, check first vertex of edge
273 VectorSubtract(info->center, vert[i], point);
274 dist = sqrt(DotProduct(point, point));
275 if (info->bestdist > dist)
277 info->bestdist = dist;
278 VectorScale(point, (info->radius / dist), info->nudge);
291 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
295 if (((mleaf_t *)node)->nummarksurfaces)
296 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
300 float f = PlaneDiff(info->center, node->plane);
301 if (f >= -info->bestdist)
302 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
303 if (f <= info->bestdist)
304 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
308 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
311 findnonsolidlocationinfo_t info;
317 VectorCopy(in, info.center);
318 info.radius = radius;
323 VectorClear(info.nudge);
324 info.bestdist = radius;
325 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
326 VectorAdd(info.center, info.nudge, info.center);
328 while (info.bestdist < radius && ++i < 10);
329 VectorCopy(info.center, out);
332 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
334 switch(nativecontents)
339 return SUPERCONTENTS_SOLID;
341 return SUPERCONTENTS_WATER;
343 return SUPERCONTENTS_SLIME;
345 return SUPERCONTENTS_LAVA;
347 return SUPERCONTENTS_SKY;
352 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
354 if (supercontents & SUPERCONTENTS_SOLID)
355 return CONTENTS_SOLID;
356 if (supercontents & SUPERCONTENTS_SKY)
358 if (supercontents & SUPERCONTENTS_LAVA)
359 return CONTENTS_LAVA;
360 if (supercontents & SUPERCONTENTS_SLIME)
361 return CONTENTS_SLIME;
362 if (supercontents & SUPERCONTENTS_WATER)
363 return CONTENTS_WATER;
364 return CONTENTS_EMPTY;
369 // the hull we're tracing through
372 // the trace structure to fill in
375 // start, end, and end - start (in model space)
380 RecursiveHullCheckTraceInfo_t;
382 // 1/32 epsilon to keep floating point happy
383 #define DIST_EPSILON (0.03125)
385 #define HULLCHECKSTATE_EMPTY 0
386 #define HULLCHECKSTATE_SOLID 1
387 #define HULLCHECKSTATE_DONE 2
389 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
391 // status variables, these don't need to be saved on the stack when
392 // recursing... but are because this should be thread-safe
393 // (note: tracing against a bbox is not thread-safe, yet)
398 // variables that need to be stored on the stack when recursing
403 // LordHavoc: a goto! everyone flee in terror... :)
408 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
409 if (!t->trace->startfound)
411 t->trace->startfound = true;
412 t->trace->startsupercontents |= num;
414 if (num & SUPERCONTENTS_LIQUIDSMASK)
415 t->trace->inwater = true;
417 t->trace->inopen = true;
418 if (num & t->trace->hitsupercontentsmask)
420 // if the first leaf is solid, set startsolid
421 if (t->trace->allsolid)
422 t->trace->startsolid = true;
423 return HULLCHECKSTATE_SOLID;
427 t->trace->allsolid = false;
428 return HULLCHECKSTATE_EMPTY;
432 // find the point distances
433 node = t->hull->clipnodes + num;
435 plane = t->hull->planes + node->planenum;
438 t1 = p1[plane->type] - plane->dist;
439 t2 = p2[plane->type] - plane->dist;
443 t1 = DotProduct (plane->normal, p1) - plane->dist;
444 t2 = DotProduct (plane->normal, p2) - plane->dist;
451 num = node->children[1];
460 num = node->children[0];
466 // the line intersects, find intersection point
467 // LordHavoc: this uses the original trace for maximum accuracy
470 t1 = t->start[plane->type] - plane->dist;
471 t2 = t->end[plane->type] - plane->dist;
475 t1 = DotProduct (plane->normal, t->start) - plane->dist;
476 t2 = DotProduct (plane->normal, t->end) - plane->dist;
479 midf = t1 / (t1 - t2);
480 midf = bound(p1f, midf, p2f);
481 VectorMA(t->start, midf, t->dist, mid);
483 // recurse both sides, front side first
484 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
485 // if this side is not empty, return what it is (solid or done)
486 if (ret != HULLCHECKSTATE_EMPTY)
489 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
490 // if other side is not solid, return what it is (empty or done)
491 if (ret != HULLCHECKSTATE_SOLID)
494 // front is air and back is solid, this is the impact point...
497 t->trace->plane.dist = -plane->dist;
498 VectorNegate (plane->normal, t->trace->plane.normal);
502 t->trace->plane.dist = plane->dist;
503 VectorCopy (plane->normal, t->trace->plane.normal);
506 // bias away from surface a bit
507 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
508 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
510 midf = t1 / (t1 - t2);
511 t->trace->fraction = bound(0.0f, midf, 1.0);
513 return HULLCHECKSTATE_DONE;
516 static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
518 // this function currently only supports same size start and end
520 RecursiveHullCheckTraceInfo_t rhc;
522 memset(&rhc, 0, sizeof(rhc));
523 memset(trace, 0, sizeof(trace_t));
525 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
526 rhc.trace->fraction = 1;
527 rhc.trace->allsolid = true;
528 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
530 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
531 else if (model->brush.ishlbsp)
533 if (boxsize[0] <= 32)
535 if (boxsize[2] < 54) // pick the nearest of 36 or 72
536 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
538 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
541 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
545 if (boxsize[0] <= 32)
546 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
548 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
550 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
551 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
552 VectorSubtract(rhc.end, rhc.start, rhc.dist);
553 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
556 static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
558 int side, distz = endz - startz;
563 if (node->contents < 0)
564 return false; // didn't hit anything
566 switch (node->plane->type)
569 node = node->children[x < node->plane->dist];
572 node = node->children[y < node->plane->dist];
575 side = startz < node->plane->dist;
576 if ((endz < node->plane->dist) == side)
578 node = node->children[side];
581 // found an intersection
582 mid = node->plane->dist;
585 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
586 front += startz * node->plane->normal[2];
587 back += endz * node->plane->normal[2];
588 side = front < node->plane->dist;
589 if ((back < node->plane->dist) == side)
591 node = node->children[side];
594 // found an intersection
595 mid = startz + distz * (front - node->plane->dist) / (front - back);
599 // go down front side
600 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
601 return true; // hit something
604 // check for impact on this node
605 if (node->numsurfaces)
610 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
611 for (i = 0;i < node->numsurfaces;i++, surf++)
613 if (!(surf->flags & SURF_LIGHTMAP))
614 continue; // no lightmaps
616 ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3]);
617 dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]);
619 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
622 ds -= surf->texturemins[0];
623 dt -= surf->texturemins[1];
625 if (ds > surf->extents[0] || dt > surf->extents[1])
631 int maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
632 line3 = ((surf->extents[0]>>4)+1)*3;
633 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
635 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
637 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
639 scale = d_lightstylevalue[surf->styles[maps]];
640 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
641 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
642 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
643 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
648 LordHavoc: here's the readable version of the interpolation
649 code, not quite as easy for the compiler to optimize...
651 dsfrac is the X position in the lightmap pixel, * 16
652 dtfrac is the Y position in the lightmap pixel, * 16
653 r00 is top left corner, r01 is top right corner
654 r10 is bottom left corner, r11 is bottom right corner
655 g and b are the same layout.
656 r0 and r1 are the top and bottom intermediate results
658 first we interpolate the top two points, to get the top
661 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
662 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
663 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
665 then we interpolate the bottom two points, to get the
668 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
669 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
670 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
672 then we interpolate the top and bottom samples to get the
673 middle sample (the one which was requested)
675 r = (((r1-r0) * dtfrac) >> 4) + r0;
676 g = (((g1-g0) * dtfrac) >> 4) + g0;
677 b = (((b1-b0) * dtfrac) >> 4) + b0;
680 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
681 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
682 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
684 return true; // success
689 node = node->children[side ^ 1];
691 distz = endz - startz;
696 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
698 Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, cl.worldmodel->brushq1.nodes + cl.worldmodel->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
701 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
708 Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
716 for (c = *in++;c > 0;c--)
720 Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
729 static void Mod_Q1BSP_LoadTextures(lump_t *l)
731 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
733 texture_t *tx, *tx2, *anims[10], *altanims[10];
735 qbyte *data, *mtdata;
738 loadmodel->brushq1.textures = NULL;
740 // add two slots for notexture walls and notexture liquids
743 m = (dmiptexlump_t *)(mod_base + l->fileofs);
744 m->nummiptex = LittleLong (m->nummiptex);
745 loadmodel->brushq1.numtextures = m->nummiptex + 2;
750 loadmodel->brushq1.numtextures = 2;
753 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
755 // fill out all slots with notexture
756 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
759 strcpy(tx->name, "NO TEXTURE FOUND");
762 tx->skin.base = r_notexture;
763 tx->shader = &Cshader_wall_lightmap;
764 tx->flags = SURF_SOLIDCLIP;
765 if (i == loadmodel->brushq1.numtextures - 1)
767 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
768 tx->shader = &Cshader_water;
770 tx->currentframe = tx;
776 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
778 // LordHavoc: mostly rewritten map texture loader
779 for (i = 0;i < m->nummiptex;i++)
781 dofs[i] = LittleLong(dofs[i]);
782 if (dofs[i] == -1 || r_nosurftextures.integer)
784 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
786 // make sure name is no more than 15 characters
787 for (j = 0;dmiptex->name[j] && j < 15;j++)
788 name[j] = dmiptex->name[j];
791 mtwidth = LittleLong(dmiptex->width);
792 mtheight = LittleLong(dmiptex->height);
794 j = LittleLong(dmiptex->offsets[0]);
798 if (j < 40 || j + mtwidth * mtheight > l->filelen)
800 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
803 mtdata = (qbyte *)dmiptex + j;
806 if ((mtwidth & 15) || (mtheight & 15))
807 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
809 // LordHavoc: force all names to lowercase
810 for (j = 0;name[j];j++)
811 if (name[j] >= 'A' && name[j] <= 'Z')
812 name[j] += 'a' - 'A';
814 tx = loadmodel->brushq1.textures + i;
815 strcpy(tx->name, name);
817 tx->height = mtheight;
821 sprintf(tx->name, "unnamed%i", i);
822 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
825 // LordHavoc: HL sky textures are entirely different than quake
826 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
828 if (loadmodel->isworldmodel)
830 data = loadimagepixels(tx->name, false, 0, 0);
833 if (image_width == 256 && image_height == 128)
841 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
843 R_InitSky(mtdata, 1);
846 else if (mtdata != NULL)
847 R_InitSky(mtdata, 1);
852 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
854 // did not find external texture, load it from the bsp or wad3
855 if (loadmodel->brush.ishlbsp)
857 // internal texture overrides wad
858 qbyte *pixels, *freepixels, *fogpixels;
859 pixels = freepixels = NULL;
861 pixels = W_ConvertWAD3Texture(dmiptex);
863 pixels = freepixels = W_GetTexture(tx->name);
866 tx->width = image_width;
867 tx->height = image_height;
868 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);
869 if (Image_CheckAlpha(pixels, image_width * image_height, true))
871 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
872 for (j = 0;j < image_width * image_height * 4;j += 4)
874 fogpixels[j + 0] = 255;
875 fogpixels[j + 1] = 255;
876 fogpixels[j + 2] = 255;
877 fogpixels[j + 3] = pixels[j + 3];
879 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
884 Mem_Free(freepixels);
886 else if (mtdata) // texture included
887 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
890 if (tx->skin.base == NULL)
895 tx->skin.base = r_notexture;
898 if (tx->name[0] == '*')
900 // turb does not block movement
901 tx->flags &= ~SURF_SOLIDCLIP;
902 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
903 // LordHavoc: some turbulent textures should be fullbright and solid
904 if (!strncmp(tx->name,"*lava",5)
905 || !strncmp(tx->name,"*teleport",9)
906 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
907 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
909 tx->flags |= SURF_WATERALPHA;
910 tx->shader = &Cshader_water;
912 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
914 tx->flags |= SURF_DRAWSKY;
915 tx->shader = &Cshader_sky;
919 tx->flags |= SURF_LIGHTMAP;
921 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
922 tx->shader = &Cshader_wall_lightmap;
925 // start out with no animation
926 tx->currentframe = tx;
929 // sequence the animations
930 for (i = 0;i < m->nummiptex;i++)
932 tx = loadmodel->brushq1.textures + i;
933 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
935 if (tx->anim_total[0] || tx->anim_total[1])
936 continue; // already sequenced
938 // find the number of frames in the animation
939 memset(anims, 0, sizeof(anims));
940 memset(altanims, 0, sizeof(altanims));
942 for (j = i;j < m->nummiptex;j++)
944 tx2 = loadmodel->brushq1.textures + j;
945 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
949 if (num >= '0' && num <= '9')
950 anims[num - '0'] = tx2;
951 else if (num >= 'a' && num <= 'j')
952 altanims[num - 'a'] = tx2;
954 Con_Printf("Bad animating texture %s\n", tx->name);
958 for (j = 0;j < 10;j++)
965 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
968 for (j = 0;j < max;j++)
972 Con_Printf("Missing frame %i of %s\n", j, tx->name);
976 for (j = 0;j < altmax;j++)
980 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
989 // if there is no alternate animation, duplicate the primary
990 // animation into the alternate
992 for (k = 0;k < 10;k++)
993 altanims[k] = anims[k];
996 // link together the primary animation
997 for (j = 0;j < max;j++)
1000 tx2->animated = true;
1001 tx2->anim_total[0] = max;
1002 tx2->anim_total[1] = altmax;
1003 for (k = 0;k < 10;k++)
1005 tx2->anim_frames[0][k] = anims[k];
1006 tx2->anim_frames[1][k] = altanims[k];
1010 // if there really is an alternate anim...
1011 if (anims[0] != altanims[0])
1013 // link together the alternate animation
1014 for (j = 0;j < altmax;j++)
1017 tx2->animated = true;
1018 // the primary/alternate are reversed here
1019 tx2->anim_total[0] = altmax;
1020 tx2->anim_total[1] = max;
1021 for (k = 0;k < 10;k++)
1023 tx2->anim_frames[0][k] = altanims[k];
1024 tx2->anim_frames[1][k] = anims[k];
1031 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1034 qbyte *in, *out, *data, d;
1035 char litfilename[1024];
1036 loadmodel->brushq1.lightdata = NULL;
1037 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1039 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1040 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1042 else // LordHavoc: bsp version 29 (normal white lighting)
1044 // LordHavoc: hope is not lost yet, check for a .lit file to load
1045 strcpy(litfilename, loadmodel->name);
1046 FS_StripExtension(litfilename, litfilename);
1047 strcat(litfilename, ".lit");
1048 data = (qbyte*) FS_LoadFile(litfilename, false);
1051 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1053 i = LittleLong(((int *)data)[1]);
1056 Con_DPrintf("loaded %s\n", litfilename);
1057 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1058 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1064 Con_Printf("Unknown .lit file version (%d)\n", i);
1070 if (fs_filesize == 8)
1071 Con_Printf("Empty .lit file, ignoring\n");
1073 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1077 // LordHavoc: oh well, expand the white lighting data
1080 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1081 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1082 out = loadmodel->brushq1.lightdata;
1083 memcpy(in, mod_base + l->fileofs, l->filelen);
1084 for (i = 0;i < l->filelen;i++)
1094 static void Mod_Q1BSP_LoadLightList(void)
1096 int a, n, numlights;
1097 char lightsfilename[1024], *s, *t, *lightsstring;
1100 strcpy(lightsfilename, loadmodel->name);
1101 FS_StripExtension(lightsfilename, lightsfilename);
1102 strcat(lightsfilename, ".lights");
1103 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1109 while (*s && *s != '\n')
1113 Mem_Free(lightsstring);
1114 Host_Error("lights file must end with a newline\n");
1119 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1122 while (*s && n < numlights)
1125 while (*s && *s != '\n')
1129 Mem_Free(lightsstring);
1130 Host_Error("misparsed lights file!\n");
1132 e = loadmodel->brushq1.lights + n;
1134 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);
1138 Mem_Free(lightsstring);
1139 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);
1146 Mem_Free(lightsstring);
1147 Host_Error("misparsed lights file!\n");
1149 loadmodel->brushq1.numlights = numlights;
1150 Mem_Free(lightsstring);
1154 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1156 loadmodel->brushq1.num_compressedpvs = 0;
1157 loadmodel->brushq1.data_compressedpvs = NULL;
1160 loadmodel->brushq1.num_compressedpvs = l->filelen;
1161 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1162 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1165 // used only for HalfLife maps
1166 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1168 char key[128], value[4096];
1173 if (!COM_ParseToken(&data, false))
1175 if (com_token[0] != '{')
1179 if (!COM_ParseToken(&data, false))
1181 if (com_token[0] == '}')
1182 break; // end of worldspawn
1183 if (com_token[0] == '_')
1184 strcpy(key, com_token + 1);
1186 strcpy(key, com_token);
1187 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1188 key[strlen(key)-1] = 0;
1189 if (!COM_ParseToken(&data, false))
1191 strcpy(value, com_token);
1192 if (!strcmp("wad", key)) // for HalfLife maps
1194 if (loadmodel->brush.ishlbsp)
1197 for (i = 0;i < 4096;i++)
1198 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1204 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1205 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1207 else if (value[i] == ';' || value[i] == 0)
1211 strcpy(wadname, "textures/");
1212 strcat(wadname, &value[j]);
1213 W_LoadTextureWadFile(wadname, false);
1225 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1227 loadmodel->brush.entities = NULL;
1230 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1231 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1232 if (loadmodel->brush.ishlbsp)
1233 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1237 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1243 in = (void *)(mod_base + l->fileofs);
1244 if (l->filelen % sizeof(*in))
1245 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1246 count = l->filelen / sizeof(*in);
1247 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1249 loadmodel->brushq1.vertexes = out;
1250 loadmodel->brushq1.numvertexes = count;
1252 for ( i=0 ; i<count ; i++, in++, out++)
1254 out->position[0] = LittleFloat(in->point[0]);
1255 out->position[1] = LittleFloat(in->point[1]);
1256 out->position[2] = LittleFloat(in->point[2]);
1260 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1266 in = (void *)(mod_base + l->fileofs);
1267 if (l->filelen % sizeof(*in))
1268 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1269 count = l->filelen / sizeof(*in);
1270 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1272 loadmodel->brushq1.submodels = out;
1273 loadmodel->brush.numsubmodels = count;
1275 for ( i=0 ; i<count ; i++, in++, out++)
1277 for (j=0 ; j<3 ; j++)
1279 // spread the mins / maxs by a pixel
1280 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1281 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1282 out->origin[j] = LittleFloat(in->origin[j]);
1284 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1285 out->headnode[j] = LittleLong(in->headnode[j]);
1286 out->visleafs = LittleLong(in->visleafs);
1287 out->firstface = LittleLong(in->firstface);
1288 out->numfaces = LittleLong(in->numfaces);
1292 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1298 in = (void *)(mod_base + l->fileofs);
1299 if (l->filelen % sizeof(*in))
1300 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1301 count = l->filelen / sizeof(*in);
1302 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1304 loadmodel->brushq1.edges = out;
1305 loadmodel->brushq1.numedges = count;
1307 for ( i=0 ; i<count ; i++, in++, out++)
1309 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1310 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1314 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1318 int i, j, k, count, miptex;
1320 in = (void *)(mod_base + l->fileofs);
1321 if (l->filelen % sizeof(*in))
1322 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1323 count = l->filelen / sizeof(*in);
1324 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1326 loadmodel->brushq1.texinfo = out;
1327 loadmodel->brushq1.numtexinfo = count;
1329 for (i = 0;i < count;i++, in++, out++)
1331 for (k = 0;k < 2;k++)
1332 for (j = 0;j < 4;j++)
1333 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1335 miptex = LittleLong(in->miptex);
1336 out->flags = LittleLong(in->flags);
1338 out->texture = NULL;
1339 if (loadmodel->brushq1.textures)
1341 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1342 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1344 out->texture = loadmodel->brushq1.textures + miptex;
1346 if (out->flags & TEX_SPECIAL)
1348 // if texture chosen is NULL or the shader needs a lightmap,
1349 // force to notexture water shader
1350 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1351 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1355 // if texture chosen is NULL, force to notexture
1356 if (out->texture == NULL)
1357 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1363 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1368 mins[0] = mins[1] = mins[2] = 9999;
1369 maxs[0] = maxs[1] = maxs[2] = -9999;
1371 for (i = 0;i < numverts;i++)
1373 for (j = 0;j < 3;j++, v++)
1383 #define MAX_SUBDIVPOLYTRIANGLES 4096
1384 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1386 static int subdivpolyverts, subdivpolytriangles;
1387 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1388 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1390 static int subdivpolylookupvert(vec3_t v)
1393 for (i = 0;i < subdivpolyverts;i++)
1394 if (subdivpolyvert[i][0] == v[0]
1395 && subdivpolyvert[i][1] == v[1]
1396 && subdivpolyvert[i][2] == v[2])
1398 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1399 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1400 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1401 return subdivpolyverts++;
1404 static void SubdividePolygon(int numverts, float *verts)
1406 int i, i1, i2, i3, f, b, c, p;
1407 vec3_t mins, maxs, front[256], back[256];
1408 float m, *pv, *cv, dist[256], frac;
1411 Host_Error("SubdividePolygon: ran out of verts in buffer");
1413 BoundPoly(numverts, verts, mins, maxs);
1415 for (i = 0;i < 3;i++)
1417 m = (mins[i] + maxs[i]) * 0.5;
1418 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1419 if (maxs[i] - m < 8)
1421 if (m - mins[i] < 8)
1425 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1426 dist[c] = cv[i] - m;
1429 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1433 VectorCopy(pv, front[f]);
1438 VectorCopy(pv, back[b]);
1441 if (dist[p] == 0 || dist[c] == 0)
1443 if ((dist[p] > 0) != (dist[c] > 0) )
1446 frac = dist[p] / (dist[p] - dist[c]);
1447 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1448 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1449 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1455 SubdividePolygon(f, front[0]);
1456 SubdividePolygon(b, back[0]);
1460 i1 = subdivpolylookupvert(verts);
1461 i2 = subdivpolylookupvert(verts + 3);
1462 for (i = 2;i < numverts;i++)
1464 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1466 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1470 i3 = subdivpolylookupvert(verts + i * 3);
1471 subdivpolyindex[subdivpolytriangles][0] = i1;
1472 subdivpolyindex[subdivpolytriangles][1] = i2;
1473 subdivpolyindex[subdivpolytriangles][2] = i3;
1475 subdivpolytriangles++;
1479 //Breaks a polygon up along axial 64 unit
1480 //boundaries so that turbulent and sky warps
1481 //can be done reasonably.
1482 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1488 subdivpolytriangles = 0;
1489 subdivpolyverts = 0;
1490 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1491 if (subdivpolytriangles < 1)
1492 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1494 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1495 mesh->numverts = subdivpolyverts;
1496 mesh->numtriangles = subdivpolytriangles;
1497 mesh->vertex = (surfvertex_t *)(mesh + 1);
1498 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1499 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1501 for (i = 0;i < mesh->numtriangles;i++)
1502 for (j = 0;j < 3;j++)
1503 mesh->index[i*3+j] = subdivpolyindex[i][j];
1505 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1507 VectorCopy(subdivpolyvert[i], v->v);
1508 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1509 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1514 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1517 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1518 mesh->numverts = numverts;
1519 mesh->numtriangles = numtriangles;
1520 mesh->vertex3f = (float *)(mesh + 1);
1521 mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1522 mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1523 mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1524 mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1525 mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1526 mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1527 mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1528 mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1529 mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1533 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1536 float *vec, *vert, mins[3], maxs[3], val, *v;
1539 // convert edges back to a normal polygon
1540 surf->poly_numverts = numedges;
1541 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1542 for (i = 0;i < numedges;i++)
1544 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1546 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1548 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1549 VectorCopy(vec, vert);
1553 // calculate polygon bounding box and center
1554 vert = surf->poly_verts;
1555 VectorCopy(vert, mins);
1556 VectorCopy(vert, maxs);
1558 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1560 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1561 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1562 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1564 VectorCopy(mins, surf->poly_mins);
1565 VectorCopy(maxs, surf->poly_maxs);
1566 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1567 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1568 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1570 // generate surface extents information
1571 tex = surf->texinfo;
1572 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1573 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1574 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1576 for (j = 0;j < 2;j++)
1578 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1585 for (i = 0;i < 2;i++)
1587 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1588 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1592 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1596 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1600 in = (void *)(mod_base + l->fileofs);
1601 if (l->filelen % sizeof(*in))
1602 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1603 count = l->filelen / sizeof(*in);
1604 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1606 loadmodel->brushq1.numsurfaces = count;
1607 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1608 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1609 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1611 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++)
1613 surf->number = surfnum;
1614 // FIXME: validate edges, texinfo, etc?
1615 firstedge = LittleLong(in->firstedge);
1616 numedges = LittleShort(in->numedges);
1617 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)
1618 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1619 i = LittleShort(in->texinfo);
1620 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1621 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1622 surf->texinfo = loadmodel->brushq1.texinfo + i;
1623 surf->flags = surf->texinfo->texture->flags;
1625 planenum = LittleShort(in->planenum);
1626 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1627 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1629 if (LittleShort(in->side))
1630 surf->flags |= SURF_PLANEBACK;
1632 surf->plane = loadmodel->brushq1.planes + planenum;
1634 // clear lightmap (filled in later)
1635 surf->lightmaptexture = NULL;
1637 // force lightmap upload on first time seeing the surface
1638 surf->cached_dlight = true;
1640 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1642 ssize = (surf->extents[0] >> 4) + 1;
1643 tsize = (surf->extents[1] >> 4) + 1;
1646 for (i = 0;i < MAXLIGHTMAPS;i++)
1647 surf->styles[i] = in->styles[i];
1648 i = LittleLong(in->lightofs);
1650 surf->samples = NULL;
1651 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1652 surf->samples = loadmodel->brushq1.lightdata + i;
1653 else // LordHavoc: white lighting (bsp version 29)
1654 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1656 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1658 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1659 Host_Error("Bad surface extents");
1660 // stainmap for permanent marks on walls
1661 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1663 memset(surf->stainsamples, 255, ssize * tsize * 3);
1667 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1668 loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1670 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++)
1672 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1673 mesh->numverts = surf->poly_numverts;
1674 mesh->numtriangles = surf->poly_numverts - 2;
1675 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1676 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1677 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1678 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1679 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1680 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1681 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1682 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1683 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1684 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1686 surf->lightmaptexturestride = 0;
1687 surf->lightmaptexture = NULL;
1689 for (i = 0;i < mesh->numverts;i++)
1691 mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1692 mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1693 mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1694 s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1695 t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1696 mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1697 mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1698 mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1699 mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1700 mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1701 mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1702 mesh->lightmapoffsets[i] = 0;
1705 for (i = 0;i < mesh->numtriangles;i++)
1707 mesh->element3i[i * 3 + 0] = 0;
1708 mesh->element3i[i * 3 + 1] = i + 1;
1709 mesh->element3i[i * 3 + 2] = i + 2;
1712 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1713 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1715 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1717 int i, iu, iv, smax, tmax;
1718 float u, v, ubase, vbase, uscale, vscale;
1720 smax = surf->extents[0] >> 4;
1721 tmax = surf->extents[1] >> 4;
1723 surf->flags |= SURF_LIGHTMAP;
1724 if (r_miplightmaps.integer)
1726 surf->lightmaptexturestride = smax+1;
1727 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);
1731 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1732 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);
1734 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1735 uscale = (uscale - ubase) / (smax + 1);
1736 vscale = (vscale - vbase) / (tmax + 1);
1738 for (i = 0;i < mesh->numverts;i++)
1740 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1741 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1742 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1743 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1744 // LordHavoc: calc lightmap data offset for vertex lighting to use
1747 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1753 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1755 node->parent = parent;
1756 if (node->contents < 0)
1758 Mod_Q1BSP_SetParent(node->children[0], node);
1759 Mod_Q1BSP_SetParent(node->children[1], node);
1762 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1768 in = (void *)(mod_base + l->fileofs);
1769 if (l->filelen % sizeof(*in))
1770 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1771 count = l->filelen / sizeof(*in);
1772 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1774 loadmodel->brushq1.nodes = out;
1775 loadmodel->brushq1.numnodes = count;
1777 for ( i=0 ; i<count ; i++, in++, out++)
1779 for (j=0 ; j<3 ; j++)
1781 out->mins[j] = LittleShort(in->mins[j]);
1782 out->maxs[j] = LittleShort(in->maxs[j]);
1785 p = LittleLong(in->planenum);
1786 out->plane = loadmodel->brushq1.planes + p;
1788 out->firstsurface = LittleShort(in->firstface);
1789 out->numsurfaces = LittleShort(in->numfaces);
1791 for (j=0 ; j<2 ; j++)
1793 p = LittleShort(in->children[j]);
1795 out->children[j] = loadmodel->brushq1.nodes + p;
1797 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1801 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1804 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1808 int i, j, count, p, pvschainbytes;
1811 in = (void *)(mod_base + l->fileofs);
1812 if (l->filelen % sizeof(*in))
1813 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1814 count = l->filelen / sizeof(*in);
1815 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1817 loadmodel->brushq1.leafs = out;
1818 loadmodel->brushq1.numleafs = count;
1819 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1820 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1822 for ( i=0 ; i<count ; i++, in++, out++)
1824 for (j=0 ; j<3 ; j++)
1826 out->mins[j] = LittleShort(in->mins[j]);
1827 out->maxs[j] = LittleShort(in->maxs[j]);
1830 // FIXME: this function could really benefit from some error checking
1832 out->contents = LittleLong(in->contents);
1834 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1835 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1838 pvs += pvschainbytes;
1840 p = LittleLong(in->visofs);
1842 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1844 memset(out->pvsdata, 0xFF, pvschainbytes);
1846 for (j = 0;j < 4;j++)
1847 out->ambient_sound_level[j] = in->ambient_level[j];
1849 // FIXME: Insert caustics here
1853 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1855 dclipnode_t *in, *out;
1859 in = (void *)(mod_base + l->fileofs);
1860 if (l->filelen % sizeof(*in))
1861 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1862 count = l->filelen / sizeof(*in);
1863 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1865 loadmodel->brushq1.clipnodes = out;
1866 loadmodel->brushq1.numclipnodes = count;
1868 if (loadmodel->brush.ishlbsp)
1870 hull = &loadmodel->brushq1.hulls[1];
1871 hull->clipnodes = out;
1872 hull->firstclipnode = 0;
1873 hull->lastclipnode = count-1;
1874 hull->planes = loadmodel->brushq1.planes;
1875 hull->clip_mins[0] = -16;
1876 hull->clip_mins[1] = -16;
1877 hull->clip_mins[2] = -36;
1878 hull->clip_maxs[0] = 16;
1879 hull->clip_maxs[1] = 16;
1880 hull->clip_maxs[2] = 36;
1881 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1883 hull = &loadmodel->brushq1.hulls[2];
1884 hull->clipnodes = out;
1885 hull->firstclipnode = 0;
1886 hull->lastclipnode = count-1;
1887 hull->planes = loadmodel->brushq1.planes;
1888 hull->clip_mins[0] = -32;
1889 hull->clip_mins[1] = -32;
1890 hull->clip_mins[2] = -32;
1891 hull->clip_maxs[0] = 32;
1892 hull->clip_maxs[1] = 32;
1893 hull->clip_maxs[2] = 32;
1894 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1896 hull = &loadmodel->brushq1.hulls[3];
1897 hull->clipnodes = out;
1898 hull->firstclipnode = 0;
1899 hull->lastclipnode = count-1;
1900 hull->planes = loadmodel->brushq1.planes;
1901 hull->clip_mins[0] = -16;
1902 hull->clip_mins[1] = -16;
1903 hull->clip_mins[2] = -18;
1904 hull->clip_maxs[0] = 16;
1905 hull->clip_maxs[1] = 16;
1906 hull->clip_maxs[2] = 18;
1907 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1911 hull = &loadmodel->brushq1.hulls[1];
1912 hull->clipnodes = out;
1913 hull->firstclipnode = 0;
1914 hull->lastclipnode = count-1;
1915 hull->planes = loadmodel->brushq1.planes;
1916 hull->clip_mins[0] = -16;
1917 hull->clip_mins[1] = -16;
1918 hull->clip_mins[2] = -24;
1919 hull->clip_maxs[0] = 16;
1920 hull->clip_maxs[1] = 16;
1921 hull->clip_maxs[2] = 32;
1922 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1924 hull = &loadmodel->brushq1.hulls[2];
1925 hull->clipnodes = out;
1926 hull->firstclipnode = 0;
1927 hull->lastclipnode = count-1;
1928 hull->planes = loadmodel->brushq1.planes;
1929 hull->clip_mins[0] = -32;
1930 hull->clip_mins[1] = -32;
1931 hull->clip_mins[2] = -24;
1932 hull->clip_maxs[0] = 32;
1933 hull->clip_maxs[1] = 32;
1934 hull->clip_maxs[2] = 64;
1935 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1938 for (i=0 ; i<count ; i++, out++, in++)
1940 out->planenum = LittleLong(in->planenum);
1941 out->children[0] = LittleShort(in->children[0]);
1942 out->children[1] = LittleShort(in->children[1]);
1943 if (out->children[0] >= count || out->children[1] >= count)
1944 Host_Error("Corrupt clipping hull(out of range child)\n");
1948 //Duplicate the drawing hull structure as a clipping hull
1949 static void Mod_Q1BSP_MakeHull0(void)
1956 hull = &loadmodel->brushq1.hulls[0];
1958 in = loadmodel->brushq1.nodes;
1959 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1961 hull->clipnodes = out;
1962 hull->firstclipnode = 0;
1963 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1964 hull->planes = loadmodel->brushq1.planes;
1966 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1968 out->planenum = in->plane - loadmodel->brushq1.planes;
1969 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1970 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1974 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1979 in = (void *)(mod_base + l->fileofs);
1980 if (l->filelen % sizeof(*in))
1981 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1982 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1983 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1985 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1987 j = (unsigned) LittleShort(in[i]);
1988 if (j >= loadmodel->brushq1.numsurfaces)
1989 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
1990 loadmodel->brushq1.marksurfaces[i] = j;
1994 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
1999 in = (void *)(mod_base + l->fileofs);
2000 if (l->filelen % sizeof(*in))
2001 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2002 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2003 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2005 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2006 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2010 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2016 in = (void *)(mod_base + l->fileofs);
2017 if (l->filelen % sizeof(*in))
2018 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2020 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2021 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2023 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2025 out->normal[0] = LittleFloat(in->normal[0]);
2026 out->normal[1] = LittleFloat(in->normal[1]);
2027 out->normal[2] = LittleFloat(in->normal[2]);
2028 out->dist = LittleFloat(in->dist);
2034 typedef struct portal_s
2037 mnode_t *nodes[2]; // [0] = front side of plane
2038 struct portal_s *next[2];
2040 struct portal_s *chain; // all portals are linked into a list
2044 static portal_t *portalchain;
2051 static portal_t *AllocPortal(void)
2054 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2055 p->chain = portalchain;
2060 static void FreePortal(portal_t *p)
2065 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2067 // calculate children first
2068 if (node->children[0]->contents >= 0)
2069 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2070 if (node->children[1]->contents >= 0)
2071 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2073 // make combined bounding box from children
2074 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2075 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2076 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2077 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2078 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2079 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2082 static void Mod_Q1BSP_FinalizePortals(void)
2084 int i, j, numportals, numpoints;
2085 portal_t *p, *pnext;
2088 mleaf_t *leaf, *endleaf;
2091 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2092 leaf = loadmodel->brushq1.leafs;
2093 endleaf = leaf + loadmodel->brushq1.numleafs;
2094 for (;leaf < endleaf;leaf++)
2096 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2097 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2104 for (i = 0;i < 2;i++)
2106 leaf = (mleaf_t *)p->nodes[i];
2108 for (j = 0;j < w->numpoints;j++)
2110 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2111 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2112 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2113 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2114 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2115 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2122 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2124 // tally up portal and point counts
2130 // note: this check must match the one below or it will usually corrupt memory
2131 // 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
2132 if (p->winding && p->nodes[0] != p->nodes[1]
2133 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2134 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2137 numpoints += p->winding->numpoints * 2;
2141 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2142 loadmodel->brushq1.numportals = numportals;
2143 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2144 loadmodel->brushq1.numportalpoints = numpoints;
2145 // clear all leaf portal chains
2146 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2147 loadmodel->brushq1.leafs[i].portals = NULL;
2148 // process all portals in the global portal chain, while freeing them
2149 portal = loadmodel->brushq1.portals;
2150 point = loadmodel->brushq1.portalpoints;
2159 // note: this check must match the one above or it will usually corrupt memory
2160 // 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
2161 if (p->nodes[0] != p->nodes[1]
2162 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2163 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2165 // first make the back to front portal(forward portal)
2166 portal->points = point;
2167 portal->numpoints = p->winding->numpoints;
2168 portal->plane.dist = p->plane.dist;
2169 VectorCopy(p->plane.normal, portal->plane.normal);
2170 portal->here = (mleaf_t *)p->nodes[1];
2171 portal->past = (mleaf_t *)p->nodes[0];
2173 for (j = 0;j < portal->numpoints;j++)
2175 VectorCopy(p->winding->points[j], point->position);
2178 PlaneClassify(&portal->plane);
2180 // link into leaf's portal chain
2181 portal->next = portal->here->portals;
2182 portal->here->portals = portal;
2184 // advance to next portal
2187 // then make the front to back portal(backward portal)
2188 portal->points = point;
2189 portal->numpoints = p->winding->numpoints;
2190 portal->plane.dist = -p->plane.dist;
2191 VectorNegate(p->plane.normal, portal->plane.normal);
2192 portal->here = (mleaf_t *)p->nodes[0];
2193 portal->past = (mleaf_t *)p->nodes[1];
2195 for (j = portal->numpoints - 1;j >= 0;j--)
2197 VectorCopy(p->winding->points[j], point->position);
2200 PlaneClassify(&portal->plane);
2202 // link into leaf's portal chain
2203 portal->next = portal->here->portals;
2204 portal->here->portals = portal;
2206 // advance to next portal
2209 Winding_Free(p->winding);
2221 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2224 Host_Error("AddPortalToNodes: NULL front node");
2226 Host_Error("AddPortalToNodes: NULL back node");
2227 if (p->nodes[0] || p->nodes[1])
2228 Host_Error("AddPortalToNodes: already included");
2229 // 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
2231 p->nodes[0] = front;
2232 p->next[0] = (portal_t *)front->portals;
2233 front->portals = (mportal_t *)p;
2236 p->next[1] = (portal_t *)back->portals;
2237 back->portals = (mportal_t *)p;
2242 RemovePortalFromNode
2245 static void RemovePortalFromNodes(portal_t *portal)
2249 void **portalpointer;
2251 for (i = 0;i < 2;i++)
2253 node = portal->nodes[i];
2255 portalpointer = (void **) &node->portals;
2260 Host_Error("RemovePortalFromNodes: portal not in leaf");
2264 if (portal->nodes[0] == node)
2266 *portalpointer = portal->next[0];
2267 portal->nodes[0] = NULL;
2269 else if (portal->nodes[1] == node)
2271 *portalpointer = portal->next[1];
2272 portal->nodes[1] = NULL;
2275 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2279 if (t->nodes[0] == node)
2280 portalpointer = (void **) &t->next[0];
2281 else if (t->nodes[1] == node)
2282 portalpointer = (void **) &t->next[1];
2284 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2289 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2292 mnode_t *front, *back, *other_node;
2293 mplane_t clipplane, *plane;
2294 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2295 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2297 // if a leaf, we're done
2301 plane = node->plane;
2303 front = node->children[0];
2304 back = node->children[1];
2306 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2308 // create the new portal by generating a polygon for the node plane,
2309 // and clipping it by all of the other portals(which came from nodes above this one)
2310 nodeportal = AllocPortal();
2311 nodeportal->plane = *node->plane;
2313 nodeportalwinding = Winding_NewFromPlane(node->plane->normal[0], node->plane->normal[1], node->plane->normal[2], node->plane->dist);
2314 side = 0; // shut up compiler warning
2315 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2317 clipplane = portal->plane;
2318 if (portal->nodes[0] == portal->nodes[1])
2319 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2320 if (portal->nodes[0] == node)
2322 else if (portal->nodes[1] == node)
2324 clipplane.dist = -clipplane.dist;
2325 VectorNegate(clipplane.normal, clipplane.normal);
2329 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2331 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2332 if (!nodeportalwinding)
2334 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2339 if (nodeportalwinding)
2341 // if the plane was not clipped on all sides, there was an error
2342 nodeportal->winding = nodeportalwinding;
2343 AddPortalToNodes(nodeportal, front, back);
2346 // split the portals of this node along this node's plane and assign them to the children of this node
2347 // (migrating the portals downward through the tree)
2348 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2350 if (portal->nodes[0] == portal->nodes[1])
2351 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2352 if (portal->nodes[0] == node)
2354 else if (portal->nodes[1] == node)
2357 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2358 nextportal = portal->next[side];
2360 other_node = portal->nodes[!side];
2361 RemovePortalFromNodes(portal);
2363 // cut the portal into two portals, one on each side of the node plane
2364 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2369 AddPortalToNodes(portal, back, other_node);
2371 AddPortalToNodes(portal, other_node, back);
2377 AddPortalToNodes(portal, front, other_node);
2379 AddPortalToNodes(portal, other_node, front);
2383 // the winding is split
2384 splitportal = AllocPortal();
2385 temp = splitportal->chain;
2386 *splitportal = *portal;
2387 splitportal->chain = temp;
2388 splitportal->winding = backwinding;
2389 Winding_Free(portal->winding);
2390 portal->winding = frontwinding;
2394 AddPortalToNodes(portal, front, other_node);
2395 AddPortalToNodes(splitportal, back, other_node);
2399 AddPortalToNodes(portal, other_node, front);
2400 AddPortalToNodes(splitportal, other_node, back);
2404 Mod_Q1BSP_RecursiveNodePortals(front);
2405 Mod_Q1BSP_RecursiveNodePortals(back);
2408 static void Mod_Q1BSP_MakePortals(void)
2411 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2412 Mod_Q1BSP_FinalizePortals();
2415 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2418 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2419 msurface_t *surf, *s;
2420 float *v0, *v1, *v2, *v3;
2421 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2422 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2423 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2425 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)
2427 if (surf->neighborsurfaces[vertnum])
2429 surf->neighborsurfaces[vertnum] = NULL;
2430 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2432 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2433 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2434 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2437 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2438 if (s->neighborsurfaces[vnum] == surf)
2440 if (vnum < s->poly_numverts)
2442 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)
2444 if (s->neighborsurfaces[vnum] == NULL
2445 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2446 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2448 surf->neighborsurfaces[vertnum] = s;
2449 s->neighborsurfaces[vnum] = surf;
2453 if (vnum < s->poly_numverts)
2461 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2463 int i, j, stylecounts[256], totalcount, remapstyles[256];
2465 memset(stylecounts, 0, sizeof(stylecounts));
2466 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2468 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2469 for (j = 0;j < MAXLIGHTMAPS;j++)
2470 stylecounts[surf->styles[j]]++;
2473 model->brushq1.light_styles = 0;
2474 for (i = 0;i < 255;i++)
2478 remapstyles[i] = model->brushq1.light_styles++;
2479 totalcount += stylecounts[i] + 1;
2484 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2485 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2486 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2487 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2488 model->brushq1.light_styles = 0;
2489 for (i = 0;i < 255;i++)
2491 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2493 for (i = 0;i < model->brushq1.light_styles;i++)
2495 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2496 j += stylecounts[model->brushq1.light_style[i]] + 1;
2498 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2500 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2501 for (j = 0;j < MAXLIGHTMAPS;j++)
2502 if (surf->styles[j] != 255)
2503 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2506 for (i = 0;i < model->brushq1.light_styles;i++)
2508 *model->brushq1.light_styleupdatechains[i] = NULL;
2509 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2510 j += stylecounts[model->brushq1.light_style[i]] + 1;
2514 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2517 for (i = 0;i < model->brushq1.numtextures;i++)
2518 model->brushq1.pvstexturechainslength[i] = 0;
2519 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2521 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2523 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2524 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2527 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2529 if (model->brushq1.pvstexturechainslength[i])
2531 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2532 j += model->brushq1.pvstexturechainslength[i] + 1;
2535 model->brushq1.pvstexturechains[i] = NULL;
2537 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2538 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2539 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2540 for (i = 0;i < model->brushq1.numtextures;i++)
2542 if (model->brushq1.pvstexturechainslength[i])
2544 *model->brushq1.pvstexturechains[i] = NULL;
2545 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2550 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2555 while (node->contents >= 0)
2557 d = PlaneDiff(org, node->plane);
2559 node = node->children[0];
2560 else if (d < -radius)
2561 node = node->children[1];
2564 // go down both sides
2565 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2566 node = node->children[1];
2570 // if this is a leaf, accumulate the pvs bits
2571 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2572 for (i = 0;i < pvsbytes;i++)
2573 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2576 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2577 //of the given point.
2578 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2580 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2581 bytes = min(bytes, pvsbufferlength);
2582 memset(pvsbuffer, 0, bytes);
2583 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2587 //Returns PVS data for a given point
2588 //(note: always returns valid data, never NULL)
2589 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2592 Mod_CheckLoaded(model);
2593 // LordHavoc: modified to start at first clip node,
2594 // in other words: first node of the (sub)model
2595 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2596 while (node->contents == 0)
2597 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2598 return ((mleaf_t *)node)->pvsdata;
2601 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2606 VectorSubtract(inmaxs, inmins, size);
2607 if (cmodel->brush.ishlbsp)
2610 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2611 else if (size[0] <= 32)
2613 if (size[2] < 54) // pick the nearest of 36 or 72
2614 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2616 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2619 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2624 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2625 else if (size[0] <= 32)
2626 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2628 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2630 VectorCopy(inmins, outmins);
2631 VectorAdd(inmins, hull->clip_size, outmaxs);
2634 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2635 extern void R_Model_Brush_Draw(entity_render_t *ent);
2636 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2637 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);
2638 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2643 mempool_t *mainmempool;
2645 model_t *originalloadmodel;
2646 float dist, modelyawradius, modelradius, *vec;
2650 mod->type = mod_brush;
2652 header = (dheader_t *)buffer;
2654 i = LittleLong(header->version);
2655 if (i != BSPVERSION && i != 30)
2656 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2657 mod->brush.ishlbsp = i == 30;
2659 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2660 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2661 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2662 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2663 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2664 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2665 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2666 mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
2667 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2668 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2669 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2670 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2672 if (loadmodel->isworldmodel)
2674 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2675 // until we get a texture for it...
2679 // swap all the lumps
2680 mod_base = (qbyte *)header;
2682 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2683 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2687 // store which lightmap format to use
2688 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2690 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2691 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2692 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2693 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2694 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2695 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2696 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2697 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2698 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2699 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2700 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2701 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2702 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2703 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2704 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2706 if (mod->brushq1.data_compressedpvs)
2707 Mem_Free(mod->brushq1.data_compressedpvs);
2708 mod->brushq1.data_compressedpvs = NULL;
2709 mod->brushq1.num_compressedpvs = 0;
2711 Mod_Q1BSP_MakeHull0();
2712 Mod_Q1BSP_MakePortals();
2714 if (developer.integer)
2715 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);
2717 mod->numframes = 2; // regular and alternate animation
2719 mainmempool = mod->mempool;
2720 loadname = mod->name;
2722 Mod_Q1BSP_LoadLightList();
2723 originalloadmodel = loadmodel;
2726 // set up the submodels(FIXME: this is confusing)
2728 for (i = 0;i < mod->brush.numsubmodels;i++)
2730 bm = &mod->brushq1.submodels[i];
2732 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2733 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2735 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2736 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2739 mod->brushq1.firstmodelsurface = bm->firstface;
2740 mod->brushq1.nummodelsurfaces = bm->numfaces;
2742 // this gets altered below if sky is used
2743 mod->DrawSky = NULL;
2744 mod->Draw = R_Model_Brush_Draw;
2745 mod->DrawFakeShadow = NULL;
2746 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2747 mod->DrawLight = R_Model_Brush_DrawLight;
2748 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2749 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2750 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2751 Mod_Q1BSP_BuildPVSTextureChains(mod);
2752 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2753 if (mod->brushq1.nummodelsurfaces)
2755 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2756 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2757 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2760 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2762 // we only need to have a drawsky function if it is used(usually only on world model)
2763 if (surf->texinfo->texture->shader == &Cshader_sky)
2764 mod->DrawSky = R_Model_Brush_DrawSky;
2765 // LordHavoc: submodels always clip, even if water
2766 if (mod->brush.numsubmodels - 1)
2767 surf->flags |= SURF_SOLIDCLIP;
2768 // calculate bounding shapes
2769 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2771 for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2773 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2774 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2775 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2776 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2777 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2778 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2779 dist = vec[0]*vec[0]+vec[1]*vec[1];
2780 if (modelyawradius < dist)
2781 modelyawradius = dist;
2782 dist += vec[2]*vec[2];
2783 if (modelradius < dist)
2788 modelyawradius = sqrt(modelyawradius);
2789 modelradius = sqrt(modelradius);
2790 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2791 mod->yawmins[2] = mod->normalmins[2];
2792 mod->yawmaxs[2] = mod->normalmaxs[2];
2793 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2794 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2795 mod->radius = modelradius;
2796 mod->radius2 = modelradius * modelradius;
2800 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2801 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2803 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2805 mod->brushq1.visleafs = bm->visleafs;
2807 // LordHavoc: only register submodels if it is the world
2808 // (prevents bsp models from replacing world submodels)
2809 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2812 // duplicate the basic information
2813 sprintf(name, "*%i", i+1);
2814 loadmodel = Mod_FindName(name);
2816 strcpy(loadmodel->name, name);
2817 // textures and memory belong to the main model
2818 loadmodel->texturepool = NULL;
2819 loadmodel->mempool = NULL;
2824 loadmodel = originalloadmodel;
2825 //Mod_Q1BSP_ProcessLightList();
2828 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2832 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2839 in = (void *)(mod_base + l->fileofs);
2840 if (l->filelen % sizeof(*in))
2841 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2842 count = l->filelen / sizeof(*in);
2843 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2846 loadmodel->num = count;
2848 for (i = 0;i < count;i++, in++, out++)
2854 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2861 in = (void *)(mod_base + l->fileofs);
2862 if (l->filelen % sizeof(*in))
2863 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2864 count = l->filelen / sizeof(*in);
2865 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2868 loadmodel->num = count;
2870 for (i = 0;i < count;i++, in++, out++)
2876 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2883 in = (void *)(mod_base + l->fileofs);
2884 if (l->filelen % sizeof(*in))
2885 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2886 count = l->filelen / sizeof(*in);
2887 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2890 loadmodel->num = count;
2892 for (i = 0;i < count;i++, in++, out++)
2898 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2905 in = (void *)(mod_base + l->fileofs);
2906 if (l->filelen % sizeof(*in))
2907 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2908 count = l->filelen / sizeof(*in);
2909 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2912 loadmodel->num = count;
2914 for (i = 0;i < count;i++, in++, out++)
2920 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2927 in = (void *)(mod_base + l->fileofs);
2928 if (l->filelen % sizeof(*in))
2929 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2930 count = l->filelen / sizeof(*in);
2931 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2934 loadmodel->num = count;
2936 for (i = 0;i < count;i++, in++, out++)
2942 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2949 in = (void *)(mod_base + l->fileofs);
2950 if (l->filelen % sizeof(*in))
2951 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2952 count = l->filelen / sizeof(*in);
2953 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2956 loadmodel->num = count;
2958 for (i = 0;i < count;i++, in++, out++)
2964 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2971 in = (void *)(mod_base + l->fileofs);
2972 if (l->filelen % sizeof(*in))
2973 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2974 count = l->filelen / sizeof(*in);
2975 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2978 loadmodel->num = count;
2980 for (i = 0;i < count;i++, in++, out++)
2986 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
2993 in = (void *)(mod_base + l->fileofs);
2994 if (l->filelen % sizeof(*in))
2995 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2996 count = l->filelen / sizeof(*in);
2997 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3000 loadmodel->num = count;
3002 for (i = 0;i < count;i++, in++, out++)
3008 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3015 in = (void *)(mod_base + l->fileofs);
3016 if (l->filelen % sizeof(*in))
3017 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3018 count = l->filelen / sizeof(*in);
3019 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3022 loadmodel->num = count;
3024 for (i = 0;i < count;i++, in++, out++)
3030 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3037 in = (void *)(mod_base + l->fileofs);
3038 if (l->filelen % sizeof(*in))
3039 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3040 count = l->filelen / sizeof(*in);
3041 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3044 loadmodel->num = count;
3046 for (i = 0;i < count;i++, in++, out++)
3052 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3059 in = (void *)(mod_base + l->fileofs);
3060 if (l->filelen % sizeof(*in))
3061 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3062 count = l->filelen / sizeof(*in);
3063 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3066 loadmodel->num = count;
3068 for (i = 0;i < count;i++, in++, out++)
3074 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3081 in = (void *)(mod_base + l->fileofs);
3082 if (l->filelen % sizeof(*in))
3083 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3084 count = l->filelen / sizeof(*in);
3085 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3088 loadmodel->num = count;
3090 for (i = 0;i < count;i++, in++, out++)
3096 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3103 in = (void *)(mod_base + l->fileofs);
3104 if (l->filelen % sizeof(*in))
3105 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3106 count = l->filelen / sizeof(*in);
3107 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3110 loadmodel->num = count;
3112 for (i = 0;i < count;i++, in++, out++)
3118 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3125 in = (void *)(mod_base + l->fileofs);
3126 if (l->filelen % sizeof(*in))
3127 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3128 count = l->filelen / sizeof(*in);
3129 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3132 loadmodel->num = count;
3134 for (i = 0;i < count;i++, in++, out++)
3140 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3147 in = (void *)(mod_base + l->fileofs);
3148 if (l->filelen % sizeof(*in))
3149 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3150 count = l->filelen / sizeof(*in);
3151 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3154 loadmodel->num = count;
3156 for (i = 0;i < count;i++, in++, out++)
3162 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3169 in = (void *)(mod_base + l->fileofs);
3170 if (l->filelen % sizeof(*in))
3171 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3172 count = l->filelen / sizeof(*in);
3173 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3176 loadmodel->num = count;
3178 for (i = 0;i < count;i++, in++, out++)
3184 static void Mod_Q2BSP_LoadModels(lump_t *l)
3191 in = (void *)(mod_base + l->fileofs);
3192 if (l->filelen % sizeof(*in))
3193 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3194 count = l->filelen / sizeof(*in);
3195 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3198 loadmodel->num = count;
3200 for (i = 0;i < count;i++, in++, out++)
3206 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3209 q2dheader_t *header;
3211 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3213 mod->type = mod_brushq2;
3215 header = (q2dheader_t *)buffer;
3217 i = LittleLong(header->version);
3218 if (i != Q2BSPVERSION)
3219 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3220 mod->brush.ishlbsp = false;
3221 if (loadmodel->isworldmodel)
3223 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3224 // until we get a texture for it...
3228 mod_base = (qbyte *)header;
3230 // swap all the lumps
3231 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3232 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3234 // store which lightmap format to use
3235 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3237 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3238 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3239 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3240 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3241 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3242 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3243 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3244 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3245 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3246 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3247 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3248 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3249 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3250 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3251 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3252 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3253 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3254 // LordHavoc: must go last because this makes the submodels
3255 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3258 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3259 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3261 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3264 char key[128], value[4096];
3266 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3267 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3268 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3271 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3272 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3273 data = loadmodel->brush.entities;
3274 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3275 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3279 if (!COM_ParseToken(&data, false))
3281 if (com_token[0] == '}')
3282 break; // end of worldspawn
3283 if (com_token[0] == '_')
3284 strcpy(key, com_token + 1);
3286 strcpy(key, com_token);
3287 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3288 key[strlen(key)-1] = 0;
3289 if (!COM_ParseToken(&data, false))
3291 strcpy(value, com_token);
3292 if (!strcmp("gridsize", key))
3294 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3295 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3301 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3307 in = (void *)(mod_base + l->fileofs);
3308 if (l->filelen % sizeof(*in))
3309 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3310 count = l->filelen / sizeof(*in);
3311 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3313 loadmodel->brushq3.data_textures = out;
3314 loadmodel->brushq3.num_textures = count;
3316 for (i = 0;i < count;i++, in++, out++)
3318 strncpy(out->name, in->name, sizeof(out->name) - 1);
3319 out->surfaceflags = LittleLong(in->surfaceflags);
3320 out->nativecontents = LittleLong(in->contents);
3321 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3322 out->renderflags = 0;
3323 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3324 out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3327 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3331 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3337 in = (void *)(mod_base + l->fileofs);
3338 if (l->filelen % sizeof(*in))
3339 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3340 count = l->filelen / sizeof(*in);
3341 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3343 loadmodel->brushq3.data_planes = out;
3344 loadmodel->brushq3.num_planes = count;
3346 for (i = 0;i < count;i++, in++, out++)
3348 out->normal[0] = LittleLong(in->normal[0]);
3349 out->normal[1] = LittleLong(in->normal[1]);
3350 out->normal[2] = LittleLong(in->normal[2]);
3351 out->dist = LittleLong(in->dist);
3356 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3359 q3mbrushside_t *out;
3362 in = (void *)(mod_base + l->fileofs);
3363 if (l->filelen % sizeof(*in))
3364 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3365 count = l->filelen / sizeof(*in);
3366 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3368 loadmodel->brushq3.data_brushsides = out;
3369 loadmodel->brushq3.num_brushsides = count;
3371 for (i = 0;i < count;i++, in++, out++)
3373 n = LittleLong(in->planeindex);
3374 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3375 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3376 out->plane = loadmodel->brushq3.data_planes + n;
3377 n = LittleLong(in->textureindex);
3378 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3379 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3380 out->texture = loadmodel->brushq3.data_textures + n;
3384 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3388 int i, j, n, c, count, maxplanes;
3391 in = (void *)(mod_base + l->fileofs);
3392 if (l->filelen % sizeof(*in))
3393 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3394 count = l->filelen / sizeof(*in);
3395 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3397 loadmodel->brushq3.data_brushes = out;
3398 loadmodel->brushq3.num_brushes = count;
3403 for (i = 0;i < count;i++, in++, out++)
3405 n = LittleLong(in->firstbrushside);
3406 c = LittleLong(in->numbrushsides);
3407 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3408 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3409 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3410 out->numbrushsides = c;
3411 n = LittleLong(in->textureindex);
3412 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3413 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3414 out->texture = loadmodel->brushq3.data_textures + n;
3416 // make a list of mplane_t structs to construct a colbrush from
3417 if (maxplanes < out->numbrushsides)
3419 maxplanes = out->numbrushsides;
3422 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3424 for (j = 0;j < out->numbrushsides;j++)
3426 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3427 planes[j].dist = out->firstbrushside[j].plane->dist;
3429 // make the colbrush from the planes
3430 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3436 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3442 in = (void *)(mod_base + l->fileofs);
3443 if (l->filelen % sizeof(*in))
3444 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3445 count = l->filelen / sizeof(*in);
3446 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3448 loadmodel->brushq3.data_effects = out;
3449 loadmodel->brushq3.num_effects = count;
3451 for (i = 0;i < count;i++, in++, out++)
3453 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3454 n = LittleLong(in->brushindex);
3455 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3456 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3457 out->brush = loadmodel->brushq3.data_brushes + n;
3458 out->unknown = LittleLong(in->unknown);
3462 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3467 in = (void *)(mod_base + l->fileofs);
3468 if (l->filelen % sizeof(*in))
3469 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3470 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3471 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3472 loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3473 loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3474 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3475 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3476 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3477 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3479 for (i = 0;i < count;i++, in++)
3481 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3482 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3483 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3484 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3485 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3486 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3487 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3488 // svector/tvector are calculated later in face loading
3489 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3490 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3491 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3492 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3493 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3494 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3495 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3496 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3497 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3498 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3499 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3500 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3501 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3505 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3511 in = (void *)(mod_base + l->fileofs);
3512 if (l->filelen % sizeof(int[3]))
3513 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3514 count = l->filelen / sizeof(*in);
3515 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3517 loadmodel->brushq3.num_triangles = count / 3;
3518 loadmodel->brushq3.data_element3i = out;
3519 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3521 for (i = 0;i < count;i++, in++, out++)
3523 *out = LittleLong(*in);
3524 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3526 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3532 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3538 in = (void *)(mod_base + l->fileofs);
3539 if (l->filelen % sizeof(*in))
3540 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3541 count = l->filelen / sizeof(*in);
3542 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3544 loadmodel->brushq3.data_lightmaps = out;
3545 loadmodel->brushq3.num_lightmaps = count;
3547 for (i = 0;i < count;i++, in++, out++)
3548 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3551 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3555 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3556 //int *originalelement3i;
3557 //int *originalneighbor3i;
3558 float *originalvertex3f;
3559 //float *originalsvector3f;
3560 //float *originaltvector3f;
3561 //float *originalnormal3f;
3562 float *originalcolor4f;
3563 float *originaltexcoordtexture2f;
3564 float *originaltexcoordlightmap2f;
3567 in = (void *)(mod_base + l->fileofs);
3568 if (l->filelen % sizeof(*in))
3569 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3570 count = l->filelen / sizeof(*in);
3571 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3573 loadmodel->brushq3.data_faces = out;
3574 loadmodel->brushq3.num_faces = count;
3576 for (i = 0;i < count;i++, in++, out++)
3578 // check face type first
3579 out->type = LittleLong(in->type);
3580 if (out->type != Q3FACETYPE_POLYGON
3581 && out->type != Q3FACETYPE_PATCH
3582 && out->type != Q3FACETYPE_MESH
3583 && out->type != Q3FACETYPE_FLARE)
3585 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3586 out->type = 0; // error
3590 n = LittleLong(in->textureindex);
3591 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3593 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3594 out->type = 0; // error
3598 out->texture = loadmodel->brushq3.data_textures + n;
3599 n = LittleLong(in->effectindex);
3600 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3602 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3608 out->effect = loadmodel->brushq3.data_effects + n;
3609 n = LittleLong(in->lightmapindex);
3610 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3612 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3616 out->lightmaptexture = NULL;
3618 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3620 out->firstvertex = LittleLong(in->firstvertex);
3621 out->numvertices = LittleLong(in->numvertices);
3622 out->firstelement = LittleLong(in->firstelement);
3623 out->numelements = LittleLong(in->numelements);
3624 out->numtriangles = out->numelements / 3;
3625 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3627 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->numvertices, loadmodel->brushq3.num_vertices);
3628 out->type = 0; // error
3631 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3633 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->numelements, loadmodel->brushq3.num_triangles * 3);
3634 out->type = 0; // error
3637 if (out->numtriangles * 3 != out->numelements)
3639 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3640 out->type = 0; // error
3643 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3644 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3645 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3646 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3647 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3648 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3649 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3650 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3651 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3654 case Q3FACETYPE_POLYGON:
3655 case Q3FACETYPE_MESH:
3656 // no processing necessary
3658 case Q3FACETYPE_PATCH:
3659 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3660 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3661 if (patchsize[0] < 1 || patchsize[1] < 1)
3663 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3664 out->type = 0; // error
3667 // convert patch to Q3FACETYPE_MESH
3668 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3669 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3670 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3671 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3672 finalvertices = finalwidth * finalheight;
3673 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3674 originalvertex3f = out->data_vertex3f;
3675 //originalsvector3f = out->data_svector3f;
3676 //originaltvector3f = out->data_tvector3f;
3677 //originalnormal3f = out->data_normal3f;
3678 originalcolor4f = out->data_color4f;
3679 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3680 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3681 //originalelement3i = out->data_element3i;
3682 //originalneighbor3i = out->data_neighbor3i;
3683 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
3684 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3685 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3686 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3687 out->data_color4f = out->data_normal3f + finalvertices * 3;
3688 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3689 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3690 out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
3691 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3692 out->type = Q3FACETYPE_MESH;
3693 out->firstvertex = -1;
3694 out->numvertices = finalvertices;
3695 out->firstelement = -1;
3696 out->numtriangles = finaltriangles;
3697 out->numelements = finaltriangles * 3;
3698 // generate geometry
3699 // (note: normals are skipped because they get recalculated)
3700 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3701 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3702 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3703 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3704 // generate elements
3705 e = out->data_element3i;
3706 for (y = 0;y < finalheight - 1;y++)
3708 row0 = (y + 0) * finalwidth;
3709 row1 = (y + 1) * finalwidth;
3710 for (x = 0;x < finalwidth - 1;x++)
3722 out->numtriangles = Mod_RemoveDegenerateTriangles(out->numtriangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3723 if (developer.integer)
3725 if (out->numtriangles < finaltriangles)
3726 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->numvertices, finaltriangles, finaltriangles - out->numtriangles, out->numtriangles);
3728 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->numvertices, out->numtriangles);
3730 // q3map does not put in collision brushes for curves... ugh
3731 out->collisions = true;
3733 case Q3FACETYPE_FLARE:
3734 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3736 out->numtriangles = 0;
3739 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3740 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3742 if (invalidelements)
3744 Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->numvertices, out->firstelement, out->numelements);
3745 for (j = 0;j < out->numelements;j++)
3747 Con_Printf(" %i", out->data_element3i[j]);
3748 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3749 out->data_element3i[j] = 0;
3753 // for shadow volumes
3754 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->numtriangles);
3755 // for per pixel lighting
3756 Mod_BuildTextureVectorsAndNormals(out->numvertices, out->numtriangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
3757 // calculate a bounding box
3758 VectorClear(out->mins);
3759 VectorClear(out->maxs);
3760 if (out->numvertices)
3762 VectorCopy(out->data_vertex3f, out->mins);
3763 VectorCopy(out->data_vertex3f, out->maxs);
3764 for (j = 1, v = out->data_vertex3f + 3;j < out->numvertices;j++, v += 3)
3766 out->mins[0] = min(out->mins[0], v[0]);
3767 out->maxs[0] = max(out->maxs[0], v[0]);
3768 out->mins[1] = min(out->mins[1], v[1]);
3769 out->maxs[1] = max(out->maxs[1], v[1]);
3770 out->mins[2] = min(out->mins[2], v[2]);
3771 out->maxs[2] = max(out->maxs[2], v[2]);
3773 out->mins[0] -= 1.0f;
3774 out->mins[1] -= 1.0f;
3775 out->mins[2] -= 1.0f;
3776 out->maxs[0] += 1.0f;
3777 out->maxs[1] += 1.0f;
3778 out->maxs[2] += 1.0f;
3783 static void Mod_Q3BSP_LoadModels(lump_t *l)
3787 int i, j, n, c, count;
3789 in = (void *)(mod_base + l->fileofs);
3790 if (l->filelen % sizeof(*in))
3791 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3792 count = l->filelen / sizeof(*in);
3793 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3795 loadmodel->brushq3.data_models = out;
3796 loadmodel->brushq3.num_models = count;
3798 for (i = 0;i < count;i++, in++, out++)
3800 for (j = 0;j < 3;j++)
3802 out->mins[j] = LittleFloat(in->mins[j]);
3803 out->maxs[j] = LittleFloat(in->maxs[j]);
3805 n = LittleLong(in->firstface);
3806 c = LittleLong(in->numfaces);
3807 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3808 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3809 out->firstface = loadmodel->brushq3.data_faces + n;
3811 n = LittleLong(in->firstbrush);
3812 c = LittleLong(in->numbrushes);
3813 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3814 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3815 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3816 out->numbrushes = c;
3820 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3826 in = (void *)(mod_base + l->fileofs);
3827 if (l->filelen % sizeof(*in))
3828 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3829 count = l->filelen / sizeof(*in);
3830 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3832 loadmodel->brushq3.data_leafbrushes = out;
3833 loadmodel->brushq3.num_leafbrushes = count;
3835 for (i = 0;i < count;i++, in++, out++)
3837 n = LittleLong(*in);
3838 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3839 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3840 *out = loadmodel->brushq3.data_brushes + n;
3844 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3850 in = (void *)(mod_base + l->fileofs);
3851 if (l->filelen % sizeof(*in))
3852 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3853 count = l->filelen / sizeof(*in);
3854 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3856 loadmodel->brushq3.data_leaffaces = out;
3857 loadmodel->brushq3.num_leaffaces = count;
3859 for (i = 0;i < count;i++, in++, out++)
3861 n = LittleLong(*in);
3862 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3863 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3864 *out = loadmodel->brushq3.data_faces + n;
3868 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3872 int i, j, n, c, count;
3874 in = (void *)(mod_base + l->fileofs);
3875 if (l->filelen % sizeof(*in))
3876 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3877 count = l->filelen / sizeof(*in);
3878 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3880 loadmodel->brushq3.data_leafs = out;
3881 loadmodel->brushq3.num_leafs = count;
3883 for (i = 0;i < count;i++, in++, out++)
3885 out->isnode = false;
3887 out->clusterindex = LittleLong(in->clusterindex);
3888 out->areaindex = LittleLong(in->areaindex);
3889 for (j = 0;j < 3;j++)
3891 // yes the mins/maxs are ints
3892 out->mins[j] = LittleLong(in->mins[j]);
3893 out->maxs[j] = LittleLong(in->maxs[j]);
3895 n = LittleLong(in->firstleafface);
3896 c = LittleLong(in->numleaffaces);
3897 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3898 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3899 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3900 out->numleaffaces = c;
3901 n = LittleLong(in->firstleafbrush);
3902 c = LittleLong(in->numleafbrushes);
3903 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3904 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3905 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3906 out->numleafbrushes = c;
3910 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3913 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3914 node->parent = parent;
3917 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3918 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3922 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3928 in = (void *)(mod_base + l->fileofs);
3929 if (l->filelen % sizeof(*in))
3930 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3931 count = l->filelen / sizeof(*in);
3932 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3934 loadmodel->brushq3.data_nodes = out;
3935 loadmodel->brushq3.num_nodes = count;
3937 for (i = 0;i < count;i++, in++, out++)
3941 n = LittleLong(in->planeindex);
3942 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3943 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3944 out->plane = loadmodel->brushq3.data_planes + n;
3945 for (j = 0;j < 2;j++)
3947 n = LittleLong(in->childrenindex[j]);
3950 if (n >= loadmodel->brushq3.num_nodes)
3951 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3952 out->children[j] = loadmodel->brushq3.data_nodes + n;
3957 if (n >= loadmodel->brushq3.num_leafs)
3958 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3959 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3962 for (j = 0;j < 3;j++)
3964 // yes the mins/maxs are ints
3965 out->mins[j] = LittleLong(in->mins[j]);
3966 out->maxs[j] = LittleLong(in->maxs[j]);
3970 // set the parent pointers
3971 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3974 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3977 q3dlightgrid_t *out;
3980 in = (void *)(mod_base + l->fileofs);
3981 if (l->filelen % sizeof(*in))
3982 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
3983 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
3984 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
3985 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
3986 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3987 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3988 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3989 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3990 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3991 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3992 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
3993 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
3994 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
3995 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
3996 if (l->filelen < count * (int)sizeof(*in))
3997 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]);
3998 if (l->filelen != count * (int)sizeof(*in))
3999 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4001 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4002 loadmodel->brushq3.data_lightgrid = out;
4003 loadmodel->brushq3.num_lightgrid = count;
4005 // no swapping or validation necessary
4006 memcpy(out, in, count * (int)sizeof(*out));
4008 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]);
4009 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]);
4012 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4017 if (l->filelen == 0)
4020 in = (void *)(mod_base + l->fileofs);
4022 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4024 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4025 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4026 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4027 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4028 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4029 if (l->filelen < totalchains + (int)sizeof(*in))
4030 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);
4032 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4033 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4036 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4038 // FIXME: finish this code
4039 VectorCopy(in, out);
4042 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4044 int i, j, k, index[3];
4045 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4046 q3dlightgrid_t *a, *s;
4047 // FIXME: write this
4048 if (!model->brushq3.num_lightgrid)
4050 ambientcolor[0] += 128;
4051 ambientcolor[1] += 128;
4052 ambientcolor[2] += 128;
4055 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4056 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4057 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4058 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0]);
4059 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1]);
4060 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2]);
4061 index[0] = (int)floor(transformed[0]);
4062 index[1] = (int)floor(transformed[1]);
4063 index[2] = (int)floor(transformed[2]);
4064 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4065 // now lerp the values
4066 VectorClear(diffusenormal);
4067 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4068 for (k = 0;k < 2;k++)
4070 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4071 for (j = 0;j < 2;j++)
4073 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4074 for (i = 0;i < 2;i++)
4076 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4077 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4078 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4079 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4080 pitch = s->diffusepitch * M_PI / 128;
4081 yaw = s->diffuseyaw * M_PI / 128;
4082 sinpitch = sin(pitch);
4083 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4084 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4085 diffusenormal[2] += blend * (cos(pitch));
4086 //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)));
4090 VectorNormalize(diffusenormal);
4091 //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]);
4094 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)
4096 int i, startside, endside;
4097 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4101 if (startfrac >= trace->fraction)
4103 // note: all line fragments past first impact fraction are ignored
4104 while (node->isnode)
4106 // recurse down node sides
4107 dist1 = PlaneDiff(start, node->plane);
4108 dist2 = PlaneDiff(end, node->plane);
4109 startside = dist1 < 0;
4110 endside = dist2 < 0;
4111 if (startside == endside)
4113 // most of the time the line fragment is on one side of the plane
4114 node = node->children[startside];
4118 // line crosses node plane, split the line
4119 midfrac = dist1 / (dist1 - dist2);
4120 VectorLerp(linestart, midfrac, lineend, mid);
4121 // take the near side first
4122 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4123 if (midfrac < trace->fraction)
4124 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4129 segmentmins[0] = min(start[0], end[0]);
4130 segmentmins[1] = min(start[1], end[1]);
4131 segmentmins[2] = min(start[2], end[2]);
4132 segmentmaxs[0] = max(start[0], end[0]);
4133 segmentmaxs[1] = max(start[1], end[1]);
4134 segmentmaxs[2] = max(start[2], end[2]);
4135 leaf = (q3mleaf_t *)node;
4136 for (i = 0;i < leaf->numleafbrushes;i++)
4138 if (startfrac >= trace->fraction)
4140 brush = leaf->firstleafbrush[i]->colbrushf;
4141 if (brush && brush->markframe != markframe)
4143 brush->markframe = markframe;
4144 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4145 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4148 if (mod_q3bsp_curves_collisions.integer)
4150 for (i = 0;i < leaf->numleaffaces;i++)
4152 if (startfrac >= trace->fraction)
4154 face = leaf->firstleafface[i];
4155 if (face->collisions && face->collisionmarkframe != markframe)
4157 face->collisionmarkframe = markframe;
4158 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4159 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4165 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)
4168 float nodesegmentmins[3], nodesegmentmaxs[3];
4172 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4173 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4174 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4175 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4176 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4177 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4178 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4182 // recurse down node sides
4183 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4186 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4187 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4189 else if (sides == 2)
4190 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4192 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4194 dist = node->plane->dist - (1.0f / 8.0f);
4195 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4197 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4199 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4205 dist = node->plane->dist + (1.0f / 8.0f);
4206 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4208 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4210 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4216 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4218 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4220 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4226 leaf = (q3mleaf_t *)node;
4227 for (i = 0;i < leaf->numleafbrushes;i++)
4229 brush = leaf->firstleafbrush[i]->colbrushf;
4230 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4232 brush->markframe = markframe;
4233 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4236 if (mod_q3bsp_curves_collisions.integer)
4238 for (i = 0;i < leaf->numleaffaces;i++)
4240 face = leaf->firstleafface[i];
4241 // 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
4242 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4243 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4249 static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
4252 float segmentmins[3], segmentmaxs[3];
4253 colbrushf_t *thisbrush_start, *thisbrush_end;
4254 matrix4x4_t startmatrix, endmatrix;
4255 static int markframe = 0;
4257 memset(trace, 0, sizeof(*trace));
4258 trace->fraction = 1;
4259 trace->hitsupercontentsmask = hitsupercontentsmask;
4260 Matrix4x4_CreateIdentity(&startmatrix);
4261 Matrix4x4_CreateIdentity(&endmatrix);
4262 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4263 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4264 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4265 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4266 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4267 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4268 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4271 if (model->brushq3.submodel)
4273 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4274 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4275 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4276 if (mod_q3bsp_curves_collisions.integer)
4278 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4280 face = model->brushq3.data_thismodel->firstface + i;
4281 if (face->collisions)
4282 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4287 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4291 // box trace, performed as brush trace
4292 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4293 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4294 if (model->brushq3.submodel)
4296 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4297 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4298 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4299 if (mod_q3bsp_curves_collisions.integer)
4301 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4303 face = model->brushq3.data_thismodel->firstface + i;
4304 if (face->collisions)
4305 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4310 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4315 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)
4322 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4323 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4326 // node - recurse down the BSP tree
4327 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4330 node = node->children[0];
4333 node = node->children[1];
4335 default: // crossing
4336 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4338 node = node->children[1];
4345 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4347 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4350 //Returns PVS data for a given point
4351 //(note: can return NULL)
4352 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4355 Mod_CheckLoaded(model);
4356 node = model->brushq3.data_nodes;
4357 while (node->isnode)
4358 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4359 if (((q3mleaf_t *)node)->clusterindex >= 0)
4360 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4365 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4371 while (node->isnode)
4373 d = PlaneDiff(org, node->plane);
4375 node = node->children[0];
4376 else if (d < -radius)
4377 node = node->children[1];
4380 // go down both sides
4381 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4382 node = node->children[1];
4385 // if this is a leaf with a pvs, accumulate the pvs bits
4386 if (((q3mleaf_t *)node)->clusterindex >= 0)
4388 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4389 for (i = 0;i < pvsbytes;i++)
4390 pvsbuffer[i] |= pvs[i];
4395 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4396 //of the given point.
4397 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4399 int bytes = model->brushq3.num_pvschainlength;
4400 bytes = min(bytes, pvsbufferlength);
4401 memset(pvsbuffer, 0, bytes);
4402 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4407 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4409 int supercontents = 0;
4410 if (nativecontents & Q2CONTENTS_SOLID)
4411 supercontents |= SUPERCONTENTS_SOLID;
4412 if (nativecontents & Q2CONTENTS_WATER)
4413 supercontents |= SUPERCONTENTS_WATER;
4414 if (nativecontents & Q2CONTENTS_SLIME)
4415 supercontents |= SUPERCONTENTS_SLIME;
4416 if (nativecontents & Q2CONTENTS_LAVA)
4417 supercontents |= SUPERCONTENTS_LAVA;
4418 return supercontents;
4421 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4423 int nativecontents = 0;
4424 if (supercontents & SUPERCONTENTS_SOLID)
4425 nativecontents |= Q2CONTENTS_SOLID;
4426 if (supercontents & SUPERCONTENTS_WATER)
4427 nativecontents |= Q2CONTENTS_WATER;
4428 if (supercontents & SUPERCONTENTS_SLIME)
4429 nativecontents |= Q2CONTENTS_SLIME;
4430 if (supercontents & SUPERCONTENTS_LAVA)
4431 nativecontents |= Q2CONTENTS_LAVA;
4432 return nativecontents;
4435 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4436 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4437 //extern void R_Q3BSP_DrawFakeShadow(struct entity_render_s *ent);
4438 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4439 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);
4440 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4443 q3dheader_t *header;
4444 float corner[3], yawradius, modelradius;
4446 mod->type = mod_brushq3;
4450 header = (q3dheader_t *)buffer;
4452 i = LittleLong(header->version);
4453 if (i != Q3BSPVERSION)
4454 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4455 if (loadmodel->isworldmodel)
4457 Cvar_SetValue("halflifebsp", false);
4458 // until we get a texture for it...
4462 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4463 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4464 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4465 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4466 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4467 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4468 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4469 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4470 //mod->DrawSky = R_Q3BSP_DrawSky;
4471 mod->Draw = R_Q3BSP_Draw;
4472 //mod->DrawFakeShadow = R_Q3BSP_DrawFakeShadow;
4473 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4474 mod->DrawLight = R_Q3BSP_DrawLight;
4476 mod_base = (qbyte *)header;
4478 // swap all the lumps
4479 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4480 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4482 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4483 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4484 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4485 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4486 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4487 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4488 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4489 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4490 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4491 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4492 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4493 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4494 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4495 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4496 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4497 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4498 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4499 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4501 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4508 // LordHavoc: only register submodels if it is the world
4509 // (prevents bsp models from replacing world submodels)
4510 if (!loadmodel->isworldmodel)
4512 // duplicate the basic information
4513 sprintf(name, "*%i", i);
4514 mod = Mod_FindName(name);
4516 strcpy(mod->name, name);
4517 // textures and memory belong to the main model
4518 mod->texturepool = NULL;
4519 mod->mempool = NULL;
4521 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4522 mod->brushq3.submodel = i;
4524 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4525 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4526 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4527 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4528 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4529 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4530 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4531 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4532 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4533 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4534 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4535 mod->yawmins[2] = mod->normalmins[2];
4536 mod->yawmaxs[2] = mod->normalmaxs[2];
4537 mod->radius = modelradius;
4538 mod->radius2 = modelradius * modelradius;
4542 void Mod_IBSP_Load(model_t *mod, void *buffer)
4544 int i = LittleLong(((int *)buffer)[1]);
4545 if (i == Q3BSPVERSION)
4546 Mod_Q3BSP_Load(mod,buffer);
4547 else if (i == Q2BSPVERSION)
4548 Mod_Q2BSP_Load(mod,buffer);
4550 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4553 void Mod_MAP_Load(model_t *mod, void *buffer)
4555 Host_Error("Mod_MAP_Load: not yet implemented\n");