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];
172 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
174 surf = info->model->brushq1.surfaces + *mark;
175 if (surf->flags & SURF_SOLIDCLIP)
178 VectorCopy(surf->plane->normal, surfnormal);
179 if (surf->flags & SURF_PLANEBACK)
180 VectorNegate(surfnormal, surfnormal);
182 for (k = 0;k < surf->mesh.num_triangles;k++)
184 tri = surf->mesh.data_element3i + k * 3;
185 VectorCopy((surf->mesh.data_vertex3f + tri[0] * 3), vert[0]);
186 VectorCopy((surf->mesh.data_vertex3f + tri[1] * 3), vert[1]);
187 VectorCopy((surf->mesh.data_vertex3f + tri[2] * 3), vert[2]);
188 VectorSubtract(vert[1], vert[0], edge[0]);
189 VectorSubtract(vert[2], vert[1], edge[1]);
190 CrossProduct(edge[1], edge[0], facenormal);
191 if (facenormal[0] || facenormal[1] || facenormal[2])
193 VectorNormalize(facenormal);
195 if (VectorDistance(facenormal, surfnormal) > 0.01f)
196 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
198 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
199 if (f <= info->bestdist && f >= -info->bestdist)
201 VectorSubtract(vert[0], vert[2], edge[2]);
202 VectorNormalize(edge[0]);
203 VectorNormalize(edge[1]);
204 VectorNormalize(edge[2]);
205 CrossProduct(facenormal, edge[0], edgenormal[0]);
206 CrossProduct(facenormal, edge[1], edgenormal[1]);
207 CrossProduct(facenormal, edge[2], edgenormal[2]);
209 if (samelevel.integer & 1)
210 VectorNegate(edgenormal[0], edgenormal[0]);
211 if (samelevel.integer & 2)
212 VectorNegate(edgenormal[1], edgenormal[1]);
213 if (samelevel.integer & 4)
214 VectorNegate(edgenormal[2], edgenormal[2]);
215 for (i = 0;i < 3;i++)
216 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
217 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
218 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
219 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]);
222 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
223 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
224 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
226 // we got lucky, the center is within the face
227 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
231 if (info->bestdist > dist)
233 info->bestdist = dist;
234 VectorScale(facenormal, (info->radius - -dist), info->nudge);
239 if (info->bestdist > dist)
241 info->bestdist = dist;
242 VectorScale(facenormal, (info->radius - dist), info->nudge);
248 // check which edge or vertex the center is nearest
249 for (i = 0;i < 3;i++)
251 f = DotProduct(info->center, edge[i]);
252 if (f >= DotProduct(vert[0], edge[i])
253 && f <= DotProduct(vert[1], edge[i]))
256 VectorMA(info->center, -f, edge[i], point);
257 dist = sqrt(DotProduct(point, point));
258 if (info->bestdist > dist)
260 info->bestdist = dist;
261 VectorScale(point, (info->radius / dist), info->nudge);
263 // skip both vertex checks
264 // (both are further away than this edge)
269 // not on edge, check first vertex of edge
270 VectorSubtract(info->center, vert[i], point);
271 dist = sqrt(DotProduct(point, point));
272 if (info->bestdist > dist)
274 info->bestdist = dist;
275 VectorScale(point, (info->radius / dist), info->nudge);
287 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
291 if (((mleaf_t *)node)->nummarksurfaces)
292 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
296 float f = PlaneDiff(info->center, node->plane);
297 if (f >= -info->bestdist)
298 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
299 if (f <= info->bestdist)
300 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
304 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
307 findnonsolidlocationinfo_t info;
313 VectorCopy(in, info.center);
314 info.radius = radius;
319 VectorClear(info.nudge);
320 info.bestdist = radius;
321 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
322 VectorAdd(info.center, info.nudge, info.center);
324 while (info.bestdist < radius && ++i < 10);
325 VectorCopy(info.center, out);
328 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
330 switch(nativecontents)
335 return SUPERCONTENTS_SOLID;
337 return SUPERCONTENTS_WATER;
339 return SUPERCONTENTS_SLIME;
341 return SUPERCONTENTS_LAVA;
343 return SUPERCONTENTS_SKY;
348 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
350 if (supercontents & SUPERCONTENTS_SOLID)
351 return CONTENTS_SOLID;
352 if (supercontents & SUPERCONTENTS_SKY)
354 if (supercontents & SUPERCONTENTS_LAVA)
355 return CONTENTS_LAVA;
356 if (supercontents & SUPERCONTENTS_SLIME)
357 return CONTENTS_SLIME;
358 if (supercontents & SUPERCONTENTS_WATER)
359 return CONTENTS_WATER;
360 return CONTENTS_EMPTY;
365 // the hull we're tracing through
368 // the trace structure to fill in
371 // start, end, and end - start (in model space)
376 RecursiveHullCheckTraceInfo_t;
378 // 1/32 epsilon to keep floating point happy
379 #define DIST_EPSILON (0.03125)
381 #define HULLCHECKSTATE_EMPTY 0
382 #define HULLCHECKSTATE_SOLID 1
383 #define HULLCHECKSTATE_DONE 2
385 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
387 // status variables, these don't need to be saved on the stack when
388 // recursing... but are because this should be thread-safe
389 // (note: tracing against a bbox is not thread-safe, yet)
394 // variables that need to be stored on the stack when recursing
399 // LordHavoc: a goto! everyone flee in terror... :)
404 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
405 if (!t->trace->startfound)
407 t->trace->startfound = true;
408 t->trace->startsupercontents |= num;
410 if (num & SUPERCONTENTS_LIQUIDSMASK)
411 t->trace->inwater = true;
413 t->trace->inopen = true;
414 if (num & t->trace->hitsupercontentsmask)
416 // if the first leaf is solid, set startsolid
417 if (t->trace->allsolid)
418 t->trace->startsolid = true;
419 return HULLCHECKSTATE_SOLID;
423 t->trace->allsolid = false;
424 return HULLCHECKSTATE_EMPTY;
428 // find the point distances
429 node = t->hull->clipnodes + num;
431 plane = t->hull->planes + node->planenum;
434 t1 = p1[plane->type] - plane->dist;
435 t2 = p2[plane->type] - plane->dist;
439 t1 = DotProduct (plane->normal, p1) - plane->dist;
440 t2 = DotProduct (plane->normal, p2) - plane->dist;
447 num = node->children[1];
456 num = node->children[0];
462 // the line intersects, find intersection point
463 // LordHavoc: this uses the original trace for maximum accuracy
466 t1 = t->start[plane->type] - plane->dist;
467 t2 = t->end[plane->type] - plane->dist;
471 t1 = DotProduct (plane->normal, t->start) - plane->dist;
472 t2 = DotProduct (plane->normal, t->end) - plane->dist;
475 midf = t1 / (t1 - t2);
476 midf = bound(p1f, midf, p2f);
477 VectorMA(t->start, midf, t->dist, mid);
479 // recurse both sides, front side first
480 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
481 // if this side is not empty, return what it is (solid or done)
482 if (ret != HULLCHECKSTATE_EMPTY)
485 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
486 // if other side is not solid, return what it is (empty or done)
487 if (ret != HULLCHECKSTATE_SOLID)
490 // front is air and back is solid, this is the impact point...
493 t->trace->plane.dist = -plane->dist;
494 VectorNegate (plane->normal, t->trace->plane.normal);
498 t->trace->plane.dist = plane->dist;
499 VectorCopy (plane->normal, t->trace->plane.normal);
502 // bias away from surface a bit
503 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
504 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
506 midf = t1 / (t1 - t2);
507 t->trace->fraction = bound(0.0f, midf, 1.0);
509 return HULLCHECKSTATE_DONE;
512 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)
514 // this function currently only supports same size start and end
516 RecursiveHullCheckTraceInfo_t rhc;
518 memset(&rhc, 0, sizeof(rhc));
519 memset(trace, 0, sizeof(trace_t));
521 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
522 rhc.trace->fraction = 1;
523 rhc.trace->allsolid = true;
524 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
526 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
527 else if (model->brush.ishlbsp)
529 if (boxsize[0] <= 32)
531 if (boxsize[2] < 54) // pick the nearest of 36 or 72
532 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
534 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
537 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
541 if (boxsize[0] <= 32)
542 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
544 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
546 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
547 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
548 VectorSubtract(rhc.end, rhc.start, rhc.dist);
549 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
552 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)
554 int side, distz = endz - startz;
559 if (node->contents < 0)
560 return false; // didn't hit anything
562 switch (node->plane->type)
565 node = node->children[x < node->plane->dist];
568 node = node->children[y < node->plane->dist];
571 side = startz < node->plane->dist;
572 if ((endz < node->plane->dist) == side)
574 node = node->children[side];
577 // found an intersection
578 mid = node->plane->dist;
581 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
582 front += startz * node->plane->normal[2];
583 back += endz * node->plane->normal[2];
584 side = front < node->plane->dist;
585 if ((back < node->plane->dist) == side)
587 node = node->children[side];
590 // found an intersection
591 mid = startz + distz * (front - node->plane->dist) / (front - back);
595 // go down front side
596 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
597 return true; // hit something
600 // check for impact on this node
601 if (node->numsurfaces)
606 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
607 for (i = 0;i < node->numsurfaces;i++, surf++)
609 if (!(surf->flags & SURF_LIGHTMAP))
610 continue; // no lightmaps
612 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]);
613 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]);
615 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
618 ds -= surf->texturemins[0];
619 dt -= surf->texturemins[1];
621 if (ds > surf->extents[0] || dt > surf->extents[1])
627 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;
628 line3 = ((surf->extents[0]>>4)+1)*3;
629 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
631 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
633 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
635 scale = d_lightstylevalue[surf->styles[maps]];
636 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
637 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
638 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
639 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
644 LordHavoc: here's the readable version of the interpolation
645 code, not quite as easy for the compiler to optimize...
647 dsfrac is the X position in the lightmap pixel, * 16
648 dtfrac is the Y position in the lightmap pixel, * 16
649 r00 is top left corner, r01 is top right corner
650 r10 is bottom left corner, r11 is bottom right corner
651 g and b are the same layout.
652 r0 and r1 are the top and bottom intermediate results
654 first we interpolate the top two points, to get the top
657 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
658 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
659 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
661 then we interpolate the bottom two points, to get the
664 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
665 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
666 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
668 then we interpolate the top and bottom samples to get the
669 middle sample (the one which was requested)
671 r = (((r1-r0) * dtfrac) >> 4) + r0;
672 g = (((g1-g0) * dtfrac) >> 4) + g0;
673 b = (((b1-b0) * dtfrac) >> 4) + b0;
676 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
677 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
678 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
680 return true; // success
685 node = node->children[side ^ 1];
687 distz = endz - startz;
692 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
694 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);
697 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
704 Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
712 for (c = *in++;c > 0;c--)
716 Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
725 static void Mod_Q1BSP_LoadTextures(lump_t *l)
727 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
729 texture_t *tx, *tx2, *anims[10], *altanims[10];
731 qbyte *data, *mtdata;
734 loadmodel->brushq1.textures = NULL;
736 // add two slots for notexture walls and notexture liquids
739 m = (dmiptexlump_t *)(mod_base + l->fileofs);
740 m->nummiptex = LittleLong (m->nummiptex);
741 loadmodel->brushq1.numtextures = m->nummiptex + 2;
746 loadmodel->brushq1.numtextures = 2;
749 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
751 // fill out all slots with notexture
752 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
755 strcpy(tx->name, "NO TEXTURE FOUND");
758 tx->skin.base = r_notexture;
759 tx->shader = &Cshader_wall_lightmap;
760 tx->flags = SURF_SOLIDCLIP;
761 if (i == loadmodel->brushq1.numtextures - 1)
763 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
764 tx->shader = &Cshader_water;
766 tx->currentframe = tx;
772 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
774 // LordHavoc: mostly rewritten map texture loader
775 for (i = 0;i < m->nummiptex;i++)
777 dofs[i] = LittleLong(dofs[i]);
778 if (dofs[i] == -1 || r_nosurftextures.integer)
780 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
782 // make sure name is no more than 15 characters
783 for (j = 0;dmiptex->name[j] && j < 15;j++)
784 name[j] = dmiptex->name[j];
787 mtwidth = LittleLong(dmiptex->width);
788 mtheight = LittleLong(dmiptex->height);
790 j = LittleLong(dmiptex->offsets[0]);
794 if (j < 40 || j + mtwidth * mtheight > l->filelen)
796 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
799 mtdata = (qbyte *)dmiptex + j;
802 if ((mtwidth & 15) || (mtheight & 15))
803 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
805 // LordHavoc: force all names to lowercase
806 for (j = 0;name[j];j++)
807 if (name[j] >= 'A' && name[j] <= 'Z')
808 name[j] += 'a' - 'A';
810 tx = loadmodel->brushq1.textures + i;
811 strcpy(tx->name, name);
813 tx->height = mtheight;
817 sprintf(tx->name, "unnamed%i", i);
818 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
821 // LordHavoc: HL sky textures are entirely different than quake
822 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
824 if (loadmodel->isworldmodel)
826 data = loadimagepixels(tx->name, false, 0, 0);
829 if (image_width == 256 && image_height == 128)
837 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
839 R_InitSky(mtdata, 1);
842 else if (mtdata != NULL)
843 R_InitSky(mtdata, 1);
848 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
850 // did not find external texture, load it from the bsp or wad3
851 if (loadmodel->brush.ishlbsp)
853 // internal texture overrides wad
854 qbyte *pixels, *freepixels, *fogpixels;
855 pixels = freepixels = NULL;
857 pixels = W_ConvertWAD3Texture(dmiptex);
859 pixels = freepixels = W_GetTexture(tx->name);
862 tx->width = image_width;
863 tx->height = image_height;
864 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);
865 if (Image_CheckAlpha(pixels, image_width * image_height, true))
867 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
868 for (j = 0;j < image_width * image_height * 4;j += 4)
870 fogpixels[j + 0] = 255;
871 fogpixels[j + 1] = 255;
872 fogpixels[j + 2] = 255;
873 fogpixels[j + 3] = pixels[j + 3];
875 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
880 Mem_Free(freepixels);
882 else if (mtdata) // texture included
883 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
886 if (tx->skin.base == NULL)
891 tx->skin.base = r_notexture;
894 if (tx->name[0] == '*')
896 // turb does not block movement
897 tx->flags &= ~SURF_SOLIDCLIP;
898 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
899 // LordHavoc: some turbulent textures should be fullbright and solid
900 if (!strncmp(tx->name,"*lava",5)
901 || !strncmp(tx->name,"*teleport",9)
902 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
903 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
905 tx->flags |= SURF_WATERALPHA;
906 tx->shader = &Cshader_water;
908 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
910 tx->flags |= SURF_DRAWSKY;
911 tx->shader = &Cshader_sky;
915 tx->flags |= SURF_LIGHTMAP;
917 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
918 tx->shader = &Cshader_wall_lightmap;
921 // start out with no animation
922 tx->currentframe = tx;
925 // sequence the animations
926 for (i = 0;i < m->nummiptex;i++)
928 tx = loadmodel->brushq1.textures + i;
929 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
931 if (tx->anim_total[0] || tx->anim_total[1])
932 continue; // already sequenced
934 // find the number of frames in the animation
935 memset(anims, 0, sizeof(anims));
936 memset(altanims, 0, sizeof(altanims));
938 for (j = i;j < m->nummiptex;j++)
940 tx2 = loadmodel->brushq1.textures + j;
941 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
945 if (num >= '0' && num <= '9')
946 anims[num - '0'] = tx2;
947 else if (num >= 'a' && num <= 'j')
948 altanims[num - 'a'] = tx2;
950 Con_Printf("Bad animating texture %s\n", tx->name);
954 for (j = 0;j < 10;j++)
961 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
964 for (j = 0;j < max;j++)
968 Con_Printf("Missing frame %i of %s\n", j, tx->name);
972 for (j = 0;j < altmax;j++)
976 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
985 // if there is no alternate animation, duplicate the primary
986 // animation into the alternate
988 for (k = 0;k < 10;k++)
989 altanims[k] = anims[k];
992 // link together the primary animation
993 for (j = 0;j < max;j++)
996 tx2->animated = true;
997 tx2->anim_total[0] = max;
998 tx2->anim_total[1] = altmax;
999 for (k = 0;k < 10;k++)
1001 tx2->anim_frames[0][k] = anims[k];
1002 tx2->anim_frames[1][k] = altanims[k];
1006 // if there really is an alternate anim...
1007 if (anims[0] != altanims[0])
1009 // link together the alternate animation
1010 for (j = 0;j < altmax;j++)
1013 tx2->animated = true;
1014 // the primary/alternate are reversed here
1015 tx2->anim_total[0] = altmax;
1016 tx2->anim_total[1] = max;
1017 for (k = 0;k < 10;k++)
1019 tx2->anim_frames[0][k] = altanims[k];
1020 tx2->anim_frames[1][k] = anims[k];
1027 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1030 qbyte *in, *out, *data, d;
1031 char litfilename[1024];
1032 loadmodel->brushq1.lightdata = NULL;
1033 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1035 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1036 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1038 else // LordHavoc: bsp version 29 (normal white lighting)
1040 // LordHavoc: hope is not lost yet, check for a .lit file to load
1041 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1042 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1043 strlcat (litfilename, ".lit", sizeof (litfilename));
1044 data = (qbyte*) FS_LoadFile(litfilename, false);
1047 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1049 i = LittleLong(((int *)data)[1]);
1052 Con_DPrintf("loaded %s\n", litfilename);
1053 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1054 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1060 Con_Printf("Unknown .lit file version (%d)\n", i);
1066 if (fs_filesize == 8)
1067 Con_Printf("Empty .lit file, ignoring\n");
1069 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1073 // LordHavoc: oh well, expand the white lighting data
1076 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1077 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1078 out = loadmodel->brushq1.lightdata;
1079 memcpy(in, mod_base + l->fileofs, l->filelen);
1080 for (i = 0;i < l->filelen;i++)
1090 static void Mod_Q1BSP_LoadLightList(void)
1092 int a, n, numlights;
1093 char lightsfilename[1024], *s, *t, *lightsstring;
1096 strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1097 FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1098 strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1099 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1105 while (*s && *s != '\n')
1109 Mem_Free(lightsstring);
1110 Host_Error("lights file must end with a newline\n");
1115 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1118 while (*s && n < numlights)
1121 while (*s && *s != '\n')
1125 Mem_Free(lightsstring);
1126 Host_Error("misparsed lights file!\n");
1128 e = loadmodel->brushq1.lights + n;
1130 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);
1134 Mem_Free(lightsstring);
1135 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);
1142 Mem_Free(lightsstring);
1143 Host_Error("misparsed lights file!\n");
1145 loadmodel->brushq1.numlights = numlights;
1146 Mem_Free(lightsstring);
1150 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1152 loadmodel->brushq1.num_compressedpvs = 0;
1153 loadmodel->brushq1.data_compressedpvs = NULL;
1156 loadmodel->brushq1.num_compressedpvs = l->filelen;
1157 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1158 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1161 // used only for HalfLife maps
1162 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1164 char key[128], value[4096];
1169 if (!COM_ParseToken(&data, false))
1171 if (com_token[0] != '{')
1175 if (!COM_ParseToken(&data, false))
1177 if (com_token[0] == '}')
1178 break; // end of worldspawn
1179 if (com_token[0] == '_')
1180 strcpy(key, com_token + 1);
1182 strcpy(key, com_token);
1183 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1184 key[strlen(key)-1] = 0;
1185 if (!COM_ParseToken(&data, false))
1187 strcpy(value, com_token);
1188 if (!strcmp("wad", key)) // for HalfLife maps
1190 if (loadmodel->brush.ishlbsp)
1193 for (i = 0;i < 4096;i++)
1194 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1200 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1201 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1203 else if (value[i] == ';' || value[i] == 0)
1207 strcpy(wadname, "textures/");
1208 strcat(wadname, &value[j]);
1209 W_LoadTextureWadFile(wadname, false);
1221 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1223 loadmodel->brush.entities = NULL;
1226 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1227 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1228 if (loadmodel->brush.ishlbsp)
1229 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1233 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1239 in = (void *)(mod_base + l->fileofs);
1240 if (l->filelen % sizeof(*in))
1241 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1242 count = l->filelen / sizeof(*in);
1243 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1245 loadmodel->brushq1.vertexes = out;
1246 loadmodel->brushq1.numvertexes = count;
1248 for ( i=0 ; i<count ; i++, in++, out++)
1250 out->position[0] = LittleFloat(in->point[0]);
1251 out->position[1] = LittleFloat(in->point[1]);
1252 out->position[2] = LittleFloat(in->point[2]);
1256 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1262 in = (void *)(mod_base + l->fileofs);
1263 if (l->filelen % sizeof(*in))
1264 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1265 count = l->filelen / sizeof(*in);
1266 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1268 loadmodel->brushq1.submodels = out;
1269 loadmodel->brush.numsubmodels = count;
1271 for ( i=0 ; i<count ; i++, in++, out++)
1273 for (j=0 ; j<3 ; j++)
1275 // spread the mins / maxs by a pixel
1276 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1277 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1278 out->origin[j] = LittleFloat(in->origin[j]);
1280 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1281 out->headnode[j] = LittleLong(in->headnode[j]);
1282 out->visleafs = LittleLong(in->visleafs);
1283 out->firstface = LittleLong(in->firstface);
1284 out->numfaces = LittleLong(in->numfaces);
1288 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1294 in = (void *)(mod_base + l->fileofs);
1295 if (l->filelen % sizeof(*in))
1296 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1297 count = l->filelen / sizeof(*in);
1298 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1300 loadmodel->brushq1.edges = out;
1301 loadmodel->brushq1.numedges = count;
1303 for ( i=0 ; i<count ; i++, in++, out++)
1305 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1306 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1310 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1314 int i, j, k, count, miptex;
1316 in = (void *)(mod_base + l->fileofs);
1317 if (l->filelen % sizeof(*in))
1318 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1319 count = l->filelen / sizeof(*in);
1320 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1322 loadmodel->brushq1.texinfo = out;
1323 loadmodel->brushq1.numtexinfo = count;
1325 for (i = 0;i < count;i++, in++, out++)
1327 for (k = 0;k < 2;k++)
1328 for (j = 0;j < 4;j++)
1329 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1331 miptex = LittleLong(in->miptex);
1332 out->flags = LittleLong(in->flags);
1334 out->texture = NULL;
1335 if (loadmodel->brushq1.textures)
1337 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1338 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1340 out->texture = loadmodel->brushq1.textures + miptex;
1342 if (out->flags & TEX_SPECIAL)
1344 // if texture chosen is NULL or the shader needs a lightmap,
1345 // force to notexture water shader
1346 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1347 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1351 // if texture chosen is NULL, force to notexture
1352 if (out->texture == NULL)
1353 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1359 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1364 mins[0] = mins[1] = mins[2] = 9999;
1365 maxs[0] = maxs[1] = maxs[2] = -9999;
1367 for (i = 0;i < numverts;i++)
1369 for (j = 0;j < 3;j++, v++)
1379 #define MAX_SUBDIVPOLYTRIANGLES 4096
1380 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1382 static int subdivpolyverts, subdivpolytriangles;
1383 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1384 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1386 static int subdivpolylookupvert(vec3_t v)
1389 for (i = 0;i < subdivpolyverts;i++)
1390 if (subdivpolyvert[i][0] == v[0]
1391 && subdivpolyvert[i][1] == v[1]
1392 && subdivpolyvert[i][2] == v[2])
1394 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1395 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1396 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1397 return subdivpolyverts++;
1400 static void SubdividePolygon(int numverts, float *verts)
1402 int i, i1, i2, i3, f, b, c, p;
1403 vec3_t mins, maxs, front[256], back[256];
1404 float m, *pv, *cv, dist[256], frac;
1407 Host_Error("SubdividePolygon: ran out of verts in buffer");
1409 BoundPoly(numverts, verts, mins, maxs);
1411 for (i = 0;i < 3;i++)
1413 m = (mins[i] + maxs[i]) * 0.5;
1414 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1415 if (maxs[i] - m < 8)
1417 if (m - mins[i] < 8)
1421 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1422 dist[c] = cv[i] - m;
1425 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1429 VectorCopy(pv, front[f]);
1434 VectorCopy(pv, back[b]);
1437 if (dist[p] == 0 || dist[c] == 0)
1439 if ((dist[p] > 0) != (dist[c] > 0) )
1442 frac = dist[p] / (dist[p] - dist[c]);
1443 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1444 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1445 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1451 SubdividePolygon(f, front[0]);
1452 SubdividePolygon(b, back[0]);
1456 i1 = subdivpolylookupvert(verts);
1457 i2 = subdivpolylookupvert(verts + 3);
1458 for (i = 2;i < numverts;i++)
1460 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1462 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1466 i3 = subdivpolylookupvert(verts + i * 3);
1467 subdivpolyindex[subdivpolytriangles][0] = i1;
1468 subdivpolyindex[subdivpolytriangles][1] = i2;
1469 subdivpolyindex[subdivpolytriangles][2] = i3;
1471 subdivpolytriangles++;
1475 //Breaks a polygon up along axial 64 unit
1476 //boundaries so that turbulent and sky warps
1477 //can be done reasonably.
1478 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1484 subdivpolytriangles = 0;
1485 subdivpolyverts = 0;
1486 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1487 if (subdivpolytriangles < 1)
1488 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1490 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1491 mesh->num_vertices = subdivpolyverts;
1492 mesh->num_triangles = subdivpolytriangles;
1493 mesh->vertex = (surfvertex_t *)(mesh + 1);
1494 mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1495 memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1497 for (i = 0;i < mesh->num_triangles;i++)
1498 for (j = 0;j < 3;j++)
1499 mesh->index[i*3+j] = subdivpolyindex[i][j];
1501 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1503 VectorCopy(subdivpolyvert[i], v->v);
1504 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1505 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1510 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1513 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1514 mesh->num_vertices = numverts;
1515 mesh->num_triangles = numtriangles;
1516 mesh->data_vertex3f = (float *)(mesh + 1);
1517 mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1518 mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1519 mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1520 mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1521 mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1522 mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1523 mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1524 mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1525 mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1529 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1532 float *vec, *vert, mins[3], maxs[3], val, *v;
1535 // convert edges back to a normal polygon
1536 surf->poly_numverts = numedges;
1537 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1538 for (i = 0;i < numedges;i++)
1540 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1542 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1544 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1545 VectorCopy(vec, vert);
1549 // calculate polygon bounding box and center
1550 vert = surf->poly_verts;
1551 VectorCopy(vert, mins);
1552 VectorCopy(vert, maxs);
1554 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1556 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1557 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1558 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1560 VectorCopy(mins, surf->poly_mins);
1561 VectorCopy(maxs, surf->poly_maxs);
1562 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1563 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1564 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1566 // generate surface extents information
1567 tex = surf->texinfo;
1568 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1569 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1570 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1572 for (j = 0;j < 2;j++)
1574 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1581 for (i = 0;i < 2;i++)
1583 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1584 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1588 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1592 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1596 in = (void *)(mod_base + l->fileofs);
1597 if (l->filelen % sizeof(*in))
1598 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1599 count = l->filelen / sizeof(*in);
1600 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1602 loadmodel->brushq1.numsurfaces = count;
1603 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1604 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1605 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1607 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++)
1609 surf->number = surfnum;
1610 // FIXME: validate edges, texinfo, etc?
1611 firstedge = LittleLong(in->firstedge);
1612 numedges = LittleShort(in->numedges);
1613 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)
1614 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1615 i = LittleShort(in->texinfo);
1616 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1617 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1618 surf->texinfo = loadmodel->brushq1.texinfo + i;
1619 surf->flags = surf->texinfo->texture->flags;
1621 planenum = LittleShort(in->planenum);
1622 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1623 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1625 if (LittleShort(in->side))
1626 surf->flags |= SURF_PLANEBACK;
1628 surf->plane = loadmodel->brushq1.planes + planenum;
1630 // clear lightmap (filled in later)
1631 surf->lightmaptexture = NULL;
1633 // force lightmap upload on first time seeing the surface
1634 surf->cached_dlight = true;
1636 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1638 ssize = (surf->extents[0] >> 4) + 1;
1639 tsize = (surf->extents[1] >> 4) + 1;
1642 for (i = 0;i < MAXLIGHTMAPS;i++)
1643 surf->styles[i] = in->styles[i];
1644 i = LittleLong(in->lightofs);
1646 surf->samples = NULL;
1647 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1648 surf->samples = loadmodel->brushq1.lightdata + i;
1649 else // LordHavoc: white lighting (bsp version 29)
1650 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1652 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1654 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1655 Host_Error("Bad surface extents");
1656 // stainmap for permanent marks on walls
1657 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1659 memset(surf->stainsamples, 255, ssize * tsize * 3);
1663 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1665 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++)
1668 mesh->num_vertices = surf->poly_numverts;
1669 mesh->num_triangles = surf->poly_numverts - 2;
1670 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1671 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1672 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1673 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1674 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1675 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1676 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1677 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1678 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1679 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1681 surf->lightmaptexturestride = 0;
1682 surf->lightmaptexture = NULL;
1684 for (i = 0;i < mesh->num_vertices;i++)
1686 mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1687 mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1688 mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1689 s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1690 t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1691 mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1692 mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1693 mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1694 mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1695 mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1696 mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1697 mesh->data_lightmapoffsets[i] = 0;
1700 for (i = 0;i < mesh->num_triangles;i++)
1702 mesh->data_element3i[i * 3 + 0] = 0;
1703 mesh->data_element3i[i * 3 + 1] = i + 1;
1704 mesh->data_element3i[i * 3 + 2] = i + 2;
1707 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1708 Mod_BuildTextureVectorsAndNormals(mesh->num_vertices, mesh->num_triangles, mesh->data_vertex3f, mesh->data_texcoordtexture2f, mesh->data_element3i, mesh->data_svector3f, mesh->data_tvector3f, mesh->data_normal3f);
1710 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1712 int i, iu, iv, smax, tmax;
1713 float u, v, ubase, vbase, uscale, vscale;
1715 smax = surf->extents[0] >> 4;
1716 tmax = surf->extents[1] >> 4;
1718 surf->flags |= SURF_LIGHTMAP;
1719 if (r_miplightmaps.integer)
1721 surf->lightmaptexturestride = smax+1;
1722 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);
1726 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1727 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);
1729 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1730 uscale = (uscale - ubase) / (smax + 1);
1731 vscale = (vscale - vbase) / (tmax + 1);
1733 for (i = 0;i < mesh->num_vertices;i++)
1735 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1736 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1737 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1738 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1739 // LordHavoc: calc lightmap data offset for vertex lighting to use
1742 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1748 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1750 node->parent = parent;
1751 if (node->contents < 0)
1753 Mod_Q1BSP_SetParent(node->children[0], node);
1754 Mod_Q1BSP_SetParent(node->children[1], node);
1757 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1763 in = (void *)(mod_base + l->fileofs);
1764 if (l->filelen % sizeof(*in))
1765 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1766 count = l->filelen / sizeof(*in);
1767 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1769 loadmodel->brushq1.nodes = out;
1770 loadmodel->brushq1.numnodes = count;
1772 for ( i=0 ; i<count ; i++, in++, out++)
1774 for (j=0 ; j<3 ; j++)
1776 out->mins[j] = LittleShort(in->mins[j]);
1777 out->maxs[j] = LittleShort(in->maxs[j]);
1780 p = LittleLong(in->planenum);
1781 out->plane = loadmodel->brushq1.planes + p;
1783 out->firstsurface = LittleShort(in->firstface);
1784 out->numsurfaces = LittleShort(in->numfaces);
1786 for (j=0 ; j<2 ; j++)
1788 p = LittleShort(in->children[j]);
1790 out->children[j] = loadmodel->brushq1.nodes + p;
1792 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1796 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1799 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1803 int i, j, count, p, pvschainbytes;
1806 in = (void *)(mod_base + l->fileofs);
1807 if (l->filelen % sizeof(*in))
1808 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1809 count = l->filelen / sizeof(*in);
1810 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1812 loadmodel->brushq1.leafs = out;
1813 loadmodel->brushq1.numleafs = count;
1814 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1815 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1817 for ( i=0 ; i<count ; i++, in++, out++)
1819 for (j=0 ; j<3 ; j++)
1821 out->mins[j] = LittleShort(in->mins[j]);
1822 out->maxs[j] = LittleShort(in->maxs[j]);
1825 // FIXME: this function could really benefit from some error checking
1827 out->contents = LittleLong(in->contents);
1829 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1830 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1831 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
1833 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid marksurface range %i:%i outside range %i:%i\n", out->firstmarksurface, out->firstmarksurface + out->nummarksurfaces, 0, loadmodel->brushq1.nummarksurfaces);
1834 out->firstmarksurface = NULL;
1835 out->nummarksurfaces = 0;
1839 memset(out->pvsdata, 0xFF, pvschainbytes);
1840 pvs += pvschainbytes;
1842 p = LittleLong(in->visofs);
1845 if (p >= loadmodel->brushq1.num_compressedpvs)
1846 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
1848 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1851 for (j = 0;j < 4;j++)
1852 out->ambient_sound_level[j] = in->ambient_level[j];
1854 // FIXME: Insert caustics here
1858 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1860 dclipnode_t *in, *out;
1864 in = (void *)(mod_base + l->fileofs);
1865 if (l->filelen % sizeof(*in))
1866 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1867 count = l->filelen / sizeof(*in);
1868 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1870 loadmodel->brushq1.clipnodes = out;
1871 loadmodel->brushq1.numclipnodes = count;
1873 if (loadmodel->brush.ishlbsp)
1875 hull = &loadmodel->brushq1.hulls[1];
1876 hull->clipnodes = out;
1877 hull->firstclipnode = 0;
1878 hull->lastclipnode = count-1;
1879 hull->planes = loadmodel->brushq1.planes;
1880 hull->clip_mins[0] = -16;
1881 hull->clip_mins[1] = -16;
1882 hull->clip_mins[2] = -36;
1883 hull->clip_maxs[0] = 16;
1884 hull->clip_maxs[1] = 16;
1885 hull->clip_maxs[2] = 36;
1886 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1888 hull = &loadmodel->brushq1.hulls[2];
1889 hull->clipnodes = out;
1890 hull->firstclipnode = 0;
1891 hull->lastclipnode = count-1;
1892 hull->planes = loadmodel->brushq1.planes;
1893 hull->clip_mins[0] = -32;
1894 hull->clip_mins[1] = -32;
1895 hull->clip_mins[2] = -32;
1896 hull->clip_maxs[0] = 32;
1897 hull->clip_maxs[1] = 32;
1898 hull->clip_maxs[2] = 32;
1899 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1901 hull = &loadmodel->brushq1.hulls[3];
1902 hull->clipnodes = out;
1903 hull->firstclipnode = 0;
1904 hull->lastclipnode = count-1;
1905 hull->planes = loadmodel->brushq1.planes;
1906 hull->clip_mins[0] = -16;
1907 hull->clip_mins[1] = -16;
1908 hull->clip_mins[2] = -18;
1909 hull->clip_maxs[0] = 16;
1910 hull->clip_maxs[1] = 16;
1911 hull->clip_maxs[2] = 18;
1912 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1916 hull = &loadmodel->brushq1.hulls[1];
1917 hull->clipnodes = out;
1918 hull->firstclipnode = 0;
1919 hull->lastclipnode = count-1;
1920 hull->planes = loadmodel->brushq1.planes;
1921 hull->clip_mins[0] = -16;
1922 hull->clip_mins[1] = -16;
1923 hull->clip_mins[2] = -24;
1924 hull->clip_maxs[0] = 16;
1925 hull->clip_maxs[1] = 16;
1926 hull->clip_maxs[2] = 32;
1927 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1929 hull = &loadmodel->brushq1.hulls[2];
1930 hull->clipnodes = out;
1931 hull->firstclipnode = 0;
1932 hull->lastclipnode = count-1;
1933 hull->planes = loadmodel->brushq1.planes;
1934 hull->clip_mins[0] = -32;
1935 hull->clip_mins[1] = -32;
1936 hull->clip_mins[2] = -24;
1937 hull->clip_maxs[0] = 32;
1938 hull->clip_maxs[1] = 32;
1939 hull->clip_maxs[2] = 64;
1940 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1943 for (i=0 ; i<count ; i++, out++, in++)
1945 out->planenum = LittleLong(in->planenum);
1946 out->children[0] = LittleShort(in->children[0]);
1947 out->children[1] = LittleShort(in->children[1]);
1948 if (out->children[0] >= count || out->children[1] >= count)
1949 Host_Error("Corrupt clipping hull(out of range child)\n");
1953 //Duplicate the drawing hull structure as a clipping hull
1954 static void Mod_Q1BSP_MakeHull0(void)
1961 hull = &loadmodel->brushq1.hulls[0];
1963 in = loadmodel->brushq1.nodes;
1964 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1966 hull->clipnodes = out;
1967 hull->firstclipnode = 0;
1968 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1969 hull->planes = loadmodel->brushq1.planes;
1971 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1973 out->planenum = in->plane - loadmodel->brushq1.planes;
1974 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1975 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1979 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1984 in = (void *)(mod_base + l->fileofs);
1985 if (l->filelen % sizeof(*in))
1986 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1987 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1988 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1990 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1992 j = (unsigned) LittleShort(in[i]);
1993 if (j >= loadmodel->brushq1.numsurfaces)
1994 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
1995 loadmodel->brushq1.marksurfaces[i] = j;
1999 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2004 in = (void *)(mod_base + l->fileofs);
2005 if (l->filelen % sizeof(*in))
2006 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2007 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2008 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2010 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2011 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2015 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2021 in = (void *)(mod_base + l->fileofs);
2022 if (l->filelen % sizeof(*in))
2023 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2025 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2026 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2028 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2030 out->normal[0] = LittleFloat(in->normal[0]);
2031 out->normal[1] = LittleFloat(in->normal[1]);
2032 out->normal[2] = LittleFloat(in->normal[2]);
2033 out->dist = LittleFloat(in->dist);
2039 typedef struct portal_s
2042 mnode_t *nodes[2]; // [0] = front side of plane
2043 struct portal_s *next[2];
2045 struct portal_s *chain; // all portals are linked into a list
2049 static portal_t *portalchain;
2056 static portal_t *AllocPortal(void)
2059 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2060 p->chain = portalchain;
2065 static void FreePortal(portal_t *p)
2070 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2072 // calculate children first
2073 if (node->children[0]->contents >= 0)
2074 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2075 if (node->children[1]->contents >= 0)
2076 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2078 // make combined bounding box from children
2079 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2080 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2081 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2082 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2083 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2084 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2087 static void Mod_Q1BSP_FinalizePortals(void)
2089 int i, j, numportals, numpoints;
2090 portal_t *p, *pnext;
2093 mleaf_t *leaf, *endleaf;
2096 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2097 leaf = loadmodel->brushq1.leafs;
2098 endleaf = leaf + loadmodel->brushq1.numleafs;
2099 for (;leaf < endleaf;leaf++)
2101 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2102 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2109 for (i = 0;i < 2;i++)
2111 leaf = (mleaf_t *)p->nodes[i];
2113 for (j = 0;j < w->numpoints;j++)
2115 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2116 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2117 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2118 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2119 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2120 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2127 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2129 // tally up portal and point counts
2135 // note: this check must match the one below or it will usually corrupt memory
2136 // 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
2137 if (p->winding && p->nodes[0] != p->nodes[1]
2138 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2139 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2142 numpoints += p->winding->numpoints * 2;
2146 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2147 loadmodel->brushq1.numportals = numportals;
2148 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2149 loadmodel->brushq1.numportalpoints = numpoints;
2150 // clear all leaf portal chains
2151 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2152 loadmodel->brushq1.leafs[i].portals = NULL;
2153 // process all portals in the global portal chain, while freeing them
2154 portal = loadmodel->brushq1.portals;
2155 point = loadmodel->brushq1.portalpoints;
2164 // note: this check must match the one above or it will usually corrupt memory
2165 // 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
2166 if (p->nodes[0] != p->nodes[1]
2167 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2168 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2170 // first make the back to front portal(forward portal)
2171 portal->points = point;
2172 portal->numpoints = p->winding->numpoints;
2173 portal->plane.dist = p->plane.dist;
2174 VectorCopy(p->plane.normal, portal->plane.normal);
2175 portal->here = (mleaf_t *)p->nodes[1];
2176 portal->past = (mleaf_t *)p->nodes[0];
2178 for (j = 0;j < portal->numpoints;j++)
2180 VectorCopy(p->winding->points[j], point->position);
2183 PlaneClassify(&portal->plane);
2185 // link into leaf's portal chain
2186 portal->next = portal->here->portals;
2187 portal->here->portals = portal;
2189 // advance to next portal
2192 // then make the front to back portal(backward portal)
2193 portal->points = point;
2194 portal->numpoints = p->winding->numpoints;
2195 portal->plane.dist = -p->plane.dist;
2196 VectorNegate(p->plane.normal, portal->plane.normal);
2197 portal->here = (mleaf_t *)p->nodes[0];
2198 portal->past = (mleaf_t *)p->nodes[1];
2200 for (j = portal->numpoints - 1;j >= 0;j--)
2202 VectorCopy(p->winding->points[j], point->position);
2205 PlaneClassify(&portal->plane);
2207 // link into leaf's portal chain
2208 portal->next = portal->here->portals;
2209 portal->here->portals = portal;
2211 // advance to next portal
2214 Winding_Free(p->winding);
2226 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2229 Host_Error("AddPortalToNodes: NULL front node");
2231 Host_Error("AddPortalToNodes: NULL back node");
2232 if (p->nodes[0] || p->nodes[1])
2233 Host_Error("AddPortalToNodes: already included");
2234 // 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
2236 p->nodes[0] = front;
2237 p->next[0] = (portal_t *)front->portals;
2238 front->portals = (mportal_t *)p;
2241 p->next[1] = (portal_t *)back->portals;
2242 back->portals = (mportal_t *)p;
2247 RemovePortalFromNode
2250 static void RemovePortalFromNodes(portal_t *portal)
2254 void **portalpointer;
2256 for (i = 0;i < 2;i++)
2258 node = portal->nodes[i];
2260 portalpointer = (void **) &node->portals;
2265 Host_Error("RemovePortalFromNodes: portal not in leaf");
2269 if (portal->nodes[0] == node)
2271 *portalpointer = portal->next[0];
2272 portal->nodes[0] = NULL;
2274 else if (portal->nodes[1] == node)
2276 *portalpointer = portal->next[1];
2277 portal->nodes[1] = NULL;
2280 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2284 if (t->nodes[0] == node)
2285 portalpointer = (void **) &t->next[0];
2286 else if (t->nodes[1] == node)
2287 portalpointer = (void **) &t->next[1];
2289 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2294 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2297 mnode_t *front, *back, *other_node;
2298 mplane_t clipplane, *plane;
2299 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2300 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2302 // if a leaf, we're done
2306 plane = node->plane;
2308 front = node->children[0];
2309 back = node->children[1];
2311 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2313 // create the new portal by generating a polygon for the node plane,
2314 // and clipping it by all of the other portals(which came from nodes above this one)
2315 nodeportal = AllocPortal();
2316 nodeportal->plane = *plane;
2318 nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2319 side = 0; // shut up compiler warning
2320 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2322 clipplane = portal->plane;
2323 if (portal->nodes[0] == portal->nodes[1])
2324 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2325 if (portal->nodes[0] == node)
2327 else if (portal->nodes[1] == node)
2329 clipplane.dist = -clipplane.dist;
2330 VectorNegate(clipplane.normal, clipplane.normal);
2334 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2336 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2337 if (!nodeportalwinding)
2339 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2344 if (nodeportalwinding)
2346 // if the plane was not clipped on all sides, there was an error
2347 nodeportal->winding = nodeportalwinding;
2348 AddPortalToNodes(nodeportal, front, back);
2351 // split the portals of this node along this node's plane and assign them to the children of this node
2352 // (migrating the portals downward through the tree)
2353 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2355 if (portal->nodes[0] == portal->nodes[1])
2356 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2357 if (portal->nodes[0] == node)
2359 else if (portal->nodes[1] == node)
2362 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2363 nextportal = portal->next[side];
2365 other_node = portal->nodes[!side];
2366 RemovePortalFromNodes(portal);
2368 // cut the portal into two portals, one on each side of the node plane
2369 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2374 AddPortalToNodes(portal, back, other_node);
2376 AddPortalToNodes(portal, other_node, back);
2382 AddPortalToNodes(portal, front, other_node);
2384 AddPortalToNodes(portal, other_node, front);
2388 // the winding is split
2389 splitportal = AllocPortal();
2390 temp = splitportal->chain;
2391 *splitportal = *portal;
2392 splitportal->chain = temp;
2393 splitportal->winding = backwinding;
2394 Winding_Free(portal->winding);
2395 portal->winding = frontwinding;
2399 AddPortalToNodes(portal, front, other_node);
2400 AddPortalToNodes(splitportal, back, other_node);
2404 AddPortalToNodes(portal, other_node, front);
2405 AddPortalToNodes(splitportal, other_node, back);
2409 Mod_Q1BSP_RecursiveNodePortals(front);
2410 Mod_Q1BSP_RecursiveNodePortals(back);
2413 static void Mod_Q1BSP_MakePortals(void)
2416 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2417 Mod_Q1BSP_FinalizePortals();
2420 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2423 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2424 msurface_t *surf, *s;
2425 float *v0, *v1, *v2, *v3;
2426 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2427 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2428 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2430 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)
2432 if (surf->neighborsurfaces[vertnum])
2434 surf->neighborsurfaces[vertnum] = NULL;
2435 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2437 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2438 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2439 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2442 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2443 if (s->neighborsurfaces[vnum] == surf)
2445 if (vnum < s->poly_numverts)
2447 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)
2449 if (s->neighborsurfaces[vnum] == NULL
2450 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2451 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2453 surf->neighborsurfaces[vertnum] = s;
2454 s->neighborsurfaces[vnum] = surf;
2458 if (vnum < s->poly_numverts)
2466 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2468 int i, j, stylecounts[256], totalcount, remapstyles[256];
2470 memset(stylecounts, 0, sizeof(stylecounts));
2471 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2473 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2474 for (j = 0;j < MAXLIGHTMAPS;j++)
2475 stylecounts[surf->styles[j]]++;
2478 model->brushq1.light_styles = 0;
2479 for (i = 0;i < 255;i++)
2483 remapstyles[i] = model->brushq1.light_styles++;
2484 totalcount += stylecounts[i] + 1;
2489 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2490 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2491 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2492 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2493 model->brushq1.light_styles = 0;
2494 for (i = 0;i < 255;i++)
2496 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2498 for (i = 0;i < model->brushq1.light_styles;i++)
2500 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2501 j += stylecounts[model->brushq1.light_style[i]] + 1;
2503 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2505 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2506 for (j = 0;j < MAXLIGHTMAPS;j++)
2507 if (surf->styles[j] != 255)
2508 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2511 for (i = 0;i < model->brushq1.light_styles;i++)
2513 *model->brushq1.light_styleupdatechains[i] = NULL;
2514 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2515 j += stylecounts[model->brushq1.light_style[i]] + 1;
2519 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2522 for (i = 0;i < model->brushq1.numtextures;i++)
2523 model->brushq1.pvstexturechainslength[i] = 0;
2524 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2526 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2528 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2529 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2532 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2534 if (model->brushq1.pvstexturechainslength[i])
2536 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2537 j += model->brushq1.pvstexturechainslength[i] + 1;
2540 model->brushq1.pvstexturechains[i] = NULL;
2542 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2543 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2544 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2545 for (i = 0;i < model->brushq1.numtextures;i++)
2547 if (model->brushq1.pvstexturechainslength[i])
2549 *model->brushq1.pvstexturechains[i] = NULL;
2550 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2555 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2560 while (node->contents >= 0)
2562 d = PlaneDiff(org, node->plane);
2564 node = node->children[0];
2565 else if (d < -radius)
2566 node = node->children[1];
2569 // go down both sides
2570 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2571 node = node->children[1];
2575 // if this is a leaf, accumulate the pvs bits
2576 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2577 for (i = 0;i < pvsbytes;i++)
2578 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2581 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2582 //of the given point.
2583 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2585 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2586 bytes = min(bytes, pvsbufferlength);
2587 memset(pvsbuffer, 0, bytes);
2588 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2592 //Returns PVS data for a given point
2593 //(note: always returns valid data, never NULL)
2594 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2597 Mod_CheckLoaded(model);
2598 // LordHavoc: modified to start at first clip node,
2599 // in other words: first node of the (sub)model
2600 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2601 while (node->contents == 0)
2602 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2603 return ((mleaf_t *)node)->pvsdata;
2606 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2611 VectorSubtract(inmaxs, inmins, size);
2612 if (cmodel->brush.ishlbsp)
2615 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2616 else if (size[0] <= 32)
2618 if (size[2] < 54) // pick the nearest of 36 or 72
2619 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2621 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2624 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2629 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2630 else if (size[0] <= 32)
2631 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2633 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2635 VectorCopy(inmins, outmins);
2636 VectorAdd(inmins, hull->clip_size, outmaxs);
2639 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2640 extern void R_Model_Brush_Draw(entity_render_t *ent);
2641 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2642 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);
2643 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2648 mempool_t *mainmempool;
2650 model_t *originalloadmodel;
2651 float dist, modelyawradius, modelradius, *vec;
2654 mod->type = mod_brush;
2656 header = (dheader_t *)buffer;
2658 i = LittleLong(header->version);
2659 if (i != BSPVERSION && i != 30)
2660 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2661 mod->brush.ishlbsp = i == 30;
2663 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2664 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2665 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2666 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2667 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2668 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2669 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2670 mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
2671 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2672 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2673 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2674 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2676 if (loadmodel->isworldmodel)
2678 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2679 // until we get a texture for it...
2683 // swap all the lumps
2684 mod_base = (qbyte *)header;
2686 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2687 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2691 // store which lightmap format to use
2692 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2694 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2695 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2696 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2697 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2698 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2699 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2700 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2701 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2702 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2703 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2704 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2705 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2706 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2707 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2708 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2710 if (mod->brushq1.data_compressedpvs)
2711 Mem_Free(mod->brushq1.data_compressedpvs);
2712 mod->brushq1.data_compressedpvs = NULL;
2713 mod->brushq1.num_compressedpvs = 0;
2715 Mod_Q1BSP_MakeHull0();
2716 Mod_Q1BSP_MakePortals();
2718 if (developer.integer)
2719 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);
2721 mod->numframes = 2; // regular and alternate animation
2723 mainmempool = mod->mempool;
2724 loadname = mod->name;
2726 Mod_Q1BSP_LoadLightList();
2727 originalloadmodel = loadmodel;
2730 // set up the submodels(FIXME: this is confusing)
2732 for (i = 0;i < mod->brush.numsubmodels;i++)
2734 bm = &mod->brushq1.submodels[i];
2736 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2737 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2739 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2740 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2743 mod->brushq1.firstmodelsurface = bm->firstface;
2744 mod->brushq1.nummodelsurfaces = bm->numfaces;
2746 // this gets altered below if sky is used
2747 mod->DrawSky = NULL;
2748 mod->Draw = R_Model_Brush_Draw;
2749 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2750 mod->DrawLight = R_Model_Brush_DrawLight;
2751 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2752 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2753 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2754 Mod_Q1BSP_BuildPVSTextureChains(mod);
2755 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2756 if (mod->brushq1.nummodelsurfaces)
2758 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2759 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2760 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2763 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2765 // we only need to have a drawsky function if it is used(usually only on world model)
2766 if (surf->texinfo->texture->shader == &Cshader_sky)
2767 mod->DrawSky = R_Model_Brush_DrawSky;
2768 // LordHavoc: submodels always clip, even if water
2769 if (mod->brush.numsubmodels - 1)
2770 surf->flags |= SURF_SOLIDCLIP;
2771 // calculate bounding shapes
2772 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
2774 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2775 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2776 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2777 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2778 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2779 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2780 dist = vec[0]*vec[0]+vec[1]*vec[1];
2781 if (modelyawradius < dist)
2782 modelyawradius = dist;
2783 dist += vec[2]*vec[2];
2784 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 strlcpy (out->name, in->name, sizeof (out->name));
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;
3325 if (!strncmp(out->name, "textures/skies/", 15))
3326 out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY;
3329 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3333 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3339 in = (void *)(mod_base + l->fileofs);
3340 if (l->filelen % sizeof(*in))
3341 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3342 count = l->filelen / sizeof(*in);
3343 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3345 loadmodel->brushq3.data_planes = out;
3346 loadmodel->brushq3.num_planes = count;
3348 for (i = 0;i < count;i++, in++, out++)
3350 out->normal[0] = LittleLong(in->normal[0]);
3351 out->normal[1] = LittleLong(in->normal[1]);
3352 out->normal[2] = LittleLong(in->normal[2]);
3353 out->dist = LittleLong(in->dist);
3358 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3361 q3mbrushside_t *out;
3364 in = (void *)(mod_base + l->fileofs);
3365 if (l->filelen % sizeof(*in))
3366 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3367 count = l->filelen / sizeof(*in);
3368 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3370 loadmodel->brushq3.data_brushsides = out;
3371 loadmodel->brushq3.num_brushsides = count;
3373 for (i = 0;i < count;i++, in++, out++)
3375 n = LittleLong(in->planeindex);
3376 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3377 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3378 out->plane = loadmodel->brushq3.data_planes + n;
3379 n = LittleLong(in->textureindex);
3380 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3381 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3382 out->texture = loadmodel->brushq3.data_textures + n;
3386 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3390 int i, j, n, c, count, maxplanes;
3393 in = (void *)(mod_base + l->fileofs);
3394 if (l->filelen % sizeof(*in))
3395 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3396 count = l->filelen / sizeof(*in);
3397 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3399 loadmodel->brushq3.data_brushes = out;
3400 loadmodel->brushq3.num_brushes = count;
3405 for (i = 0;i < count;i++, in++, out++)
3407 n = LittleLong(in->firstbrushside);
3408 c = LittleLong(in->numbrushsides);
3409 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3410 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3411 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3412 out->numbrushsides = c;
3413 n = LittleLong(in->textureindex);
3414 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3415 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3416 out->texture = loadmodel->brushq3.data_textures + n;
3418 // make a list of mplane_t structs to construct a colbrush from
3419 if (maxplanes < out->numbrushsides)
3421 maxplanes = out->numbrushsides;
3424 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3426 for (j = 0;j < out->numbrushsides;j++)
3428 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3429 planes[j].dist = out->firstbrushside[j].plane->dist;
3431 // make the colbrush from the planes
3432 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3438 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3444 in = (void *)(mod_base + l->fileofs);
3445 if (l->filelen % sizeof(*in))
3446 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3447 count = l->filelen / sizeof(*in);
3448 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3450 loadmodel->brushq3.data_effects = out;
3451 loadmodel->brushq3.num_effects = count;
3453 for (i = 0;i < count;i++, in++, out++)
3455 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3456 n = LittleLong(in->brushindex);
3457 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3458 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3459 out->brush = loadmodel->brushq3.data_brushes + n;
3460 out->unknown = LittleLong(in->unknown);
3464 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3469 in = (void *)(mod_base + l->fileofs);
3470 if (l->filelen % sizeof(*in))
3471 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3472 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3473 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3474 loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3475 loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3476 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3477 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3478 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3479 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3481 for (i = 0;i < count;i++, in++)
3483 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3484 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3485 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3486 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3487 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3488 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3489 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3490 // svector/tvector are calculated later in face loading
3491 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3492 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3493 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3494 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3495 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3496 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3497 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3498 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3499 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3500 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3501 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3502 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3503 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3507 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3513 in = (void *)(mod_base + l->fileofs);
3514 if (l->filelen % sizeof(int[3]))
3515 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3516 count = l->filelen / sizeof(*in);
3517 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3519 loadmodel->brushq3.num_triangles = count / 3;
3520 loadmodel->brushq3.data_element3i = out;
3521 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3523 for (i = 0;i < count;i++, in++, out++)
3525 *out = LittleLong(*in);
3526 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3528 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3534 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3542 in = (void *)(mod_base + l->fileofs);
3543 if (l->filelen % sizeof(*in))
3544 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3545 count = l->filelen / sizeof(*in);
3546 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3548 loadmodel->brushq3.data_lightmaps = out;
3549 loadmodel->brushq3.num_lightmaps = count;
3551 for (i = 0;i < count;i++, in++, out++)
3552 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3555 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3559 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3560 //int *originalelement3i;
3561 //int *originalneighbor3i;
3562 float *originalvertex3f;
3563 //float *originalsvector3f;
3564 //float *originaltvector3f;
3565 //float *originalnormal3f;
3566 float *originalcolor4f;
3567 float *originaltexcoordtexture2f;
3568 float *originaltexcoordlightmap2f;
3571 in = (void *)(mod_base + l->fileofs);
3572 if (l->filelen % sizeof(*in))
3573 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3574 count = l->filelen / sizeof(*in);
3575 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3577 loadmodel->brushq3.data_faces = out;
3578 loadmodel->brushq3.num_faces = count;
3580 for (i = 0;i < count;i++, in++, out++)
3582 // check face type first
3583 out->type = LittleLong(in->type);
3584 if (out->type != Q3FACETYPE_POLYGON
3585 && out->type != Q3FACETYPE_PATCH
3586 && out->type != Q3FACETYPE_MESH
3587 && out->type != Q3FACETYPE_FLARE)
3589 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3590 out->type = 0; // error
3594 n = LittleLong(in->textureindex);
3595 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3597 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3598 out->type = 0; // error
3602 out->texture = loadmodel->brushq3.data_textures + n;
3603 n = LittleLong(in->effectindex);
3604 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3606 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3612 out->effect = loadmodel->brushq3.data_effects + n;
3613 n = LittleLong(in->lightmapindex);
3614 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3616 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3620 out->lightmaptexture = NULL;
3622 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3624 out->firstvertex = LittleLong(in->firstvertex);
3625 out->num_vertices = LittleLong(in->numvertices);
3626 out->firstelement = LittleLong(in->firstelement);
3627 out->num_triangles = LittleLong(in->numelements) / 3;
3628 if (out->num_triangles * 3 != LittleLong(in->numelements))
3630 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, LittleLong(in->numelements));
3631 out->type = 0; // error
3634 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3636 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->num_vertices, loadmodel->brushq3.num_vertices);
3637 out->type = 0; // error
3640 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3642 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->num_triangles * 3, loadmodel->brushq3.num_triangles * 3);
3643 out->type = 0; // error
3646 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3647 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3648 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3649 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3650 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3651 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3652 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3653 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3654 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3657 case Q3FACETYPE_POLYGON:
3658 case Q3FACETYPE_MESH:
3659 // no processing necessary
3661 case Q3FACETYPE_PATCH:
3662 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3663 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3664 if (patchsize[0] < 1 || patchsize[1] < 1)
3666 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3667 out->type = 0; // error
3670 // convert patch to Q3FACETYPE_MESH
3671 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3672 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3673 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3674 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3675 finalvertices = finalwidth * finalheight;
3676 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3677 originalvertex3f = out->data_vertex3f;
3678 //originalsvector3f = out->data_svector3f;
3679 //originaltvector3f = out->data_tvector3f;
3680 //originalnormal3f = out->data_normal3f;
3681 originalcolor4f = out->data_color4f;
3682 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3683 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3684 //originalelement3i = out->data_element3i;
3685 //originalneighbor3i = out->data_neighbor3i;
3686 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
3687 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3688 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3689 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3690 out->data_color4f = out->data_normal3f + finalvertices * 3;
3691 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3692 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3693 out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
3694 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3695 out->type = Q3FACETYPE_MESH;
3696 out->firstvertex = -1;
3697 out->num_vertices = finalvertices;
3698 out->firstelement = -1;
3699 out->num_triangles = finaltriangles;
3700 // generate geometry
3701 // (note: normals are skipped because they get recalculated)
3702 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3703 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3704 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3705 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3706 // generate elements
3707 e = out->data_element3i;
3708 for (y = 0;y < finalheight - 1;y++)
3710 row0 = (y + 0) * finalwidth;
3711 row1 = (y + 1) * finalwidth;
3712 for (x = 0;x < finalwidth - 1;x++)
3724 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3725 if (developer.integer)
3727 if (out->num_triangles < finaltriangles)
3728 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_vertices, finaltriangles, finaltriangles - out->num_triangles, out->num_triangles);
3730 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_vertices, out->num_triangles);
3732 // q3map does not put in collision brushes for curves... ugh
3733 out->collisions = true;
3735 case Q3FACETYPE_FLARE:
3736 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3738 out->num_triangles = 0;
3741 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
3742 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3744 if (invalidelements)
3746 Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->num_vertices, out->firstelement, out->num_triangles * 3);
3747 for (j = 0;j < out->num_triangles * 3;j++)
3749 Con_Printf(" %i", out->data_element3i[j]);
3750 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3751 out->data_element3i[j] = 0;
3755 // for shadow volumes
3756 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
3757 // for per pixel lighting
3758 Mod_BuildTextureVectorsAndNormals(out->num_vertices, out->num_triangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
3759 // calculate a bounding box
3760 VectorClear(out->mins);
3761 VectorClear(out->maxs);
3762 if (out->num_vertices)
3764 VectorCopy(out->data_vertex3f, out->mins);
3765 VectorCopy(out->data_vertex3f, out->maxs);
3766 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
3768 out->mins[0] = min(out->mins[0], v[0]);
3769 out->maxs[0] = max(out->maxs[0], v[0]);
3770 out->mins[1] = min(out->mins[1], v[1]);
3771 out->maxs[1] = max(out->maxs[1], v[1]);
3772 out->mins[2] = min(out->mins[2], v[2]);
3773 out->maxs[2] = max(out->maxs[2], v[2]);
3775 out->mins[0] -= 1.0f;
3776 out->mins[1] -= 1.0f;
3777 out->mins[2] -= 1.0f;
3778 out->maxs[0] += 1.0f;
3779 out->maxs[1] += 1.0f;
3780 out->maxs[2] += 1.0f;
3785 static void Mod_Q3BSP_LoadModels(lump_t *l)
3789 int i, j, n, c, count;
3791 in = (void *)(mod_base + l->fileofs);
3792 if (l->filelen % sizeof(*in))
3793 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3794 count = l->filelen / sizeof(*in);
3795 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3797 loadmodel->brushq3.data_models = out;
3798 loadmodel->brushq3.num_models = count;
3800 for (i = 0;i < count;i++, in++, out++)
3802 for (j = 0;j < 3;j++)
3804 out->mins[j] = LittleFloat(in->mins[j]);
3805 out->maxs[j] = LittleFloat(in->maxs[j]);
3807 n = LittleLong(in->firstface);
3808 c = LittleLong(in->numfaces);
3809 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3810 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3811 out->firstface = loadmodel->brushq3.data_faces + n;
3813 n = LittleLong(in->firstbrush);
3814 c = LittleLong(in->numbrushes);
3815 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3816 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3817 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3818 out->numbrushes = c;
3822 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3828 in = (void *)(mod_base + l->fileofs);
3829 if (l->filelen % sizeof(*in))
3830 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3831 count = l->filelen / sizeof(*in);
3832 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3834 loadmodel->brushq3.data_leafbrushes = out;
3835 loadmodel->brushq3.num_leafbrushes = count;
3837 for (i = 0;i < count;i++, in++, out++)
3839 n = LittleLong(*in);
3840 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3841 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3842 *out = loadmodel->brushq3.data_brushes + n;
3846 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3852 in = (void *)(mod_base + l->fileofs);
3853 if (l->filelen % sizeof(*in))
3854 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3855 count = l->filelen / sizeof(*in);
3856 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3858 loadmodel->brushq3.data_leaffaces = out;
3859 loadmodel->brushq3.num_leaffaces = count;
3861 for (i = 0;i < count;i++, in++, out++)
3863 n = LittleLong(*in);
3864 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3865 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3866 *out = loadmodel->brushq3.data_faces + n;
3870 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3874 int i, j, n, c, count;
3876 in = (void *)(mod_base + l->fileofs);
3877 if (l->filelen % sizeof(*in))
3878 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3879 count = l->filelen / sizeof(*in);
3880 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3882 loadmodel->brushq3.data_leafs = out;
3883 loadmodel->brushq3.num_leafs = count;
3885 for (i = 0;i < count;i++, in++, out++)
3887 out->isnode = false;
3889 out->clusterindex = LittleLong(in->clusterindex);
3890 out->areaindex = LittleLong(in->areaindex);
3891 for (j = 0;j < 3;j++)
3893 // yes the mins/maxs are ints
3894 out->mins[j] = LittleLong(in->mins[j]);
3895 out->maxs[j] = LittleLong(in->maxs[j]);
3897 n = LittleLong(in->firstleafface);
3898 c = LittleLong(in->numleaffaces);
3899 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3900 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3901 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3902 out->numleaffaces = c;
3903 n = LittleLong(in->firstleafbrush);
3904 c = LittleLong(in->numleafbrushes);
3905 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3906 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3907 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3908 out->numleafbrushes = c;
3912 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3915 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3916 node->parent = parent;
3919 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3920 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3924 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3930 in = (void *)(mod_base + l->fileofs);
3931 if (l->filelen % sizeof(*in))
3932 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3933 count = l->filelen / sizeof(*in);
3934 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3936 loadmodel->brushq3.data_nodes = out;
3937 loadmodel->brushq3.num_nodes = count;
3939 for (i = 0;i < count;i++, in++, out++)
3943 n = LittleLong(in->planeindex);
3944 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3945 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3946 out->plane = loadmodel->brushq3.data_planes + n;
3947 for (j = 0;j < 2;j++)
3949 n = LittleLong(in->childrenindex[j]);
3952 if (n >= loadmodel->brushq3.num_nodes)
3953 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3954 out->children[j] = loadmodel->brushq3.data_nodes + n;
3959 if (n >= loadmodel->brushq3.num_leafs)
3960 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3961 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3964 for (j = 0;j < 3;j++)
3966 // yes the mins/maxs are ints
3967 out->mins[j] = LittleLong(in->mins[j]);
3968 out->maxs[j] = LittleLong(in->maxs[j]);
3972 // set the parent pointers
3973 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3976 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3979 q3dlightgrid_t *out;
3982 if (l->filelen == 0)
3985 in = (void *)(mod_base + l->fileofs);
3986 if (l->filelen % sizeof(*in))
3987 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
3988 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
3989 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
3990 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
3991 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3992 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3993 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3994 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3995 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3996 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3997 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
3998 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
3999 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4000 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4001 if (l->filelen < count * (int)sizeof(*in))
4002 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]);
4003 if (l->filelen != count * (int)sizeof(*in))
4004 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4006 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4007 loadmodel->brushq3.data_lightgrid = out;
4008 loadmodel->brushq3.num_lightgrid = count;
4010 // no swapping or validation necessary
4011 memcpy(out, in, count * (int)sizeof(*out));
4013 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]);
4014 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]);
4017 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4022 if (l->filelen == 0)
4025 in = (void *)(mod_base + l->fileofs);
4027 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4029 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4030 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4031 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4032 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4033 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4034 if (l->filelen < totalchains + (int)sizeof(*in))
4035 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);
4037 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4038 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4041 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4043 // FIXME: finish this code
4044 VectorCopy(in, out);
4047 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4049 int i, j, k, index[3];
4050 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4051 q3dlightgrid_t *a, *s;
4052 // FIXME: write this
4053 if (!model->brushq3.num_lightgrid)
4055 ambientcolor[0] += 128;
4056 ambientcolor[1] += 128;
4057 ambientcolor[2] += 128;
4060 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4061 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4062 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4063 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4064 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4065 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4066 index[0] = (int)floor(transformed[0]);
4067 index[1] = (int)floor(transformed[1]);
4068 index[2] = (int)floor(transformed[2]);
4069 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4070 // now lerp the values
4071 VectorClear(diffusenormal);
4072 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4073 for (k = 0;k < 2;k++)
4075 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4076 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4078 for (j = 0;j < 2;j++)
4080 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4081 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4083 for (i = 0;i < 2;i++)
4085 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4086 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4088 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4089 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4090 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4091 pitch = s->diffusepitch * M_PI / 128;
4092 yaw = s->diffuseyaw * M_PI / 128;
4093 sinpitch = sin(pitch);
4094 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4095 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4096 diffusenormal[2] += blend * (cos(pitch));
4097 //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)));
4101 VectorNormalize(diffusenormal);
4102 //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]);
4105 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)
4107 int i, startside, endside;
4108 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4112 if (startfrac >= trace->fraction)
4114 // note: all line fragments past first impact fraction are ignored
4115 while (node->isnode)
4117 // recurse down node sides
4118 dist1 = PlaneDiff(start, node->plane);
4119 dist2 = PlaneDiff(end, node->plane);
4120 startside = dist1 < 0;
4121 endside = dist2 < 0;
4122 if (startside == endside)
4124 // most of the time the line fragment is on one side of the plane
4125 node = node->children[startside];
4129 // line crosses node plane, split the line
4130 midfrac = dist1 / (dist1 - dist2);
4131 VectorLerp(linestart, midfrac, lineend, mid);
4132 // take the near side first
4133 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4134 if (midfrac < trace->fraction)
4135 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4140 segmentmins[0] = min(start[0], end[0]);
4141 segmentmins[1] = min(start[1], end[1]);
4142 segmentmins[2] = min(start[2], end[2]);
4143 segmentmaxs[0] = max(start[0], end[0]);
4144 segmentmaxs[1] = max(start[1], end[1]);
4145 segmentmaxs[2] = max(start[2], end[2]);
4146 leaf = (q3mleaf_t *)node;
4147 for (i = 0;i < leaf->numleafbrushes;i++)
4149 if (startfrac >= trace->fraction)
4151 brush = leaf->firstleafbrush[i]->colbrushf;
4152 if (brush && brush->markframe != markframe)
4154 brush->markframe = markframe;
4155 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4156 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4159 if (mod_q3bsp_curves_collisions.integer)
4161 for (i = 0;i < leaf->numleaffaces;i++)
4163 if (startfrac >= trace->fraction)
4165 face = leaf->firstleafface[i];
4166 if (face->collisions && face->collisionmarkframe != markframe)
4168 face->collisionmarkframe = markframe;
4169 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4170 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4176 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)
4179 float nodesegmentmins[3], nodesegmentmaxs[3];
4183 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4184 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4185 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4186 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4187 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4188 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4189 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4193 // recurse down node sides
4194 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4197 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4198 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4200 else if (sides == 2)
4201 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4203 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
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[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4216 dist = node->plane->dist + (1.0f / 8.0f);
4217 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4219 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4221 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4227 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4229 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4231 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4237 leaf = (q3mleaf_t *)node;
4238 for (i = 0;i < leaf->numleafbrushes;i++)
4240 brush = leaf->firstleafbrush[i]->colbrushf;
4241 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4243 brush->markframe = markframe;
4244 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4247 if (mod_q3bsp_curves_collisions.integer)
4249 for (i = 0;i < leaf->numleaffaces;i++)
4251 face = leaf->firstleafface[i];
4252 // 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
4253 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4254 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4260 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)
4263 float segmentmins[3], segmentmaxs[3];
4264 colbrushf_t *thisbrush_start, *thisbrush_end;
4265 matrix4x4_t startmatrix, endmatrix;
4266 static int markframe = 0;
4268 memset(trace, 0, sizeof(*trace));
4269 trace->fraction = 1;
4270 trace->hitsupercontentsmask = hitsupercontentsmask;
4271 Matrix4x4_CreateIdentity(&startmatrix);
4272 Matrix4x4_CreateIdentity(&endmatrix);
4273 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4274 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4275 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4276 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4277 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4278 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4279 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4282 if (model->brushq3.submodel)
4284 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4285 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4286 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4287 if (mod_q3bsp_curves_collisions.integer)
4289 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4291 face = model->brushq3.data_thismodel->firstface + i;
4292 if (face->collisions)
4293 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4298 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4302 // box trace, performed as brush trace
4303 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4304 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4305 if (model->brushq3.submodel)
4307 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4308 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4309 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4310 if (mod_q3bsp_curves_collisions.integer)
4312 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4314 face = model->brushq3.data_thismodel->firstface + i;
4315 if (face->collisions)
4316 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4321 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4326 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)
4333 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4334 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4337 // node - recurse down the BSP tree
4338 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4341 node = node->children[0];
4344 node = node->children[1];
4346 default: // crossing
4347 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4349 node = node->children[1];
4356 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4358 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4361 //Returns PVS data for a given point
4362 //(note: can return NULL)
4363 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4366 Mod_CheckLoaded(model);
4367 node = model->brushq3.data_nodes;
4368 while (node->isnode)
4369 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4370 if (((q3mleaf_t *)node)->clusterindex >= 0)
4371 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4376 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4382 while (node->isnode)
4384 d = PlaneDiff(org, node->plane);
4386 node = node->children[0];
4387 else if (d < -radius)
4388 node = node->children[1];
4391 // go down both sides
4392 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4393 node = node->children[1];
4396 // if this is a leaf with a pvs, accumulate the pvs bits
4397 if (((q3mleaf_t *)node)->clusterindex >= 0)
4399 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4400 for (i = 0;i < pvsbytes;i++)
4401 pvsbuffer[i] |= pvs[i];
4406 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4407 //of the given point.
4408 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4410 int bytes = model->brushq3.num_pvschainlength;
4411 bytes = min(bytes, pvsbufferlength);
4412 memset(pvsbuffer, 0, bytes);
4413 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4418 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4420 int supercontents = 0;
4421 if (nativecontents & Q2CONTENTS_SOLID)
4422 supercontents |= SUPERCONTENTS_SOLID;
4423 if (nativecontents & Q2CONTENTS_WATER)
4424 supercontents |= SUPERCONTENTS_WATER;
4425 if (nativecontents & Q2CONTENTS_SLIME)
4426 supercontents |= SUPERCONTENTS_SLIME;
4427 if (nativecontents & Q2CONTENTS_LAVA)
4428 supercontents |= SUPERCONTENTS_LAVA;
4429 return supercontents;
4432 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4434 int nativecontents = 0;
4435 if (supercontents & SUPERCONTENTS_SOLID)
4436 nativecontents |= Q2CONTENTS_SOLID;
4437 if (supercontents & SUPERCONTENTS_WATER)
4438 nativecontents |= Q2CONTENTS_WATER;
4439 if (supercontents & SUPERCONTENTS_SLIME)
4440 nativecontents |= Q2CONTENTS_SLIME;
4441 if (supercontents & SUPERCONTENTS_LAVA)
4442 nativecontents |= Q2CONTENTS_LAVA;
4443 return nativecontents;
4446 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4447 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4448 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4449 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);
4450 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4453 q3dheader_t *header;
4454 float corner[3], yawradius, modelradius;
4456 mod->type = mod_brushq3;
4460 header = (q3dheader_t *)buffer;
4462 i = LittleLong(header->version);
4463 if (i != Q3BSPVERSION)
4464 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4465 if (loadmodel->isworldmodel)
4467 Cvar_SetValue("halflifebsp", false);
4468 // until we get a texture for it...
4472 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4473 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4474 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4475 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4476 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4477 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4478 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4479 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4480 //mod->DrawSky = R_Q3BSP_DrawSky;
4481 mod->Draw = R_Q3BSP_Draw;
4482 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4483 mod->DrawLight = R_Q3BSP_DrawLight;
4485 mod_base = (qbyte *)header;
4487 // swap all the lumps
4488 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4489 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4491 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4492 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4493 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4494 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4495 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4496 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4497 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4498 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4499 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4500 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4501 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4502 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4503 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4504 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4505 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4506 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4507 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4508 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4510 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4517 // LordHavoc: only register submodels if it is the world
4518 // (prevents bsp models from replacing world submodels)
4519 if (!loadmodel->isworldmodel)
4521 // duplicate the basic information
4522 sprintf(name, "*%i", i);
4523 mod = Mod_FindName(name);
4525 strcpy(mod->name, name);
4526 // textures and memory belong to the main model
4527 mod->texturepool = NULL;
4528 mod->mempool = NULL;
4530 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4531 mod->brushq3.submodel = i;
4533 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4534 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4535 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4536 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4537 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4538 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4539 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4540 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4541 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4542 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4543 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4544 mod->yawmins[2] = mod->normalmins[2];
4545 mod->yawmaxs[2] = mod->normalmaxs[2];
4546 mod->radius = modelradius;
4547 mod->radius2 = modelradius * modelradius;
4551 void Mod_IBSP_Load(model_t *mod, void *buffer)
4553 int i = LittleLong(((int *)buffer)[1]);
4554 if (i == Q3BSPVERSION)
4555 Mod_Q3BSP_Load(mod,buffer);
4556 else if (i == Q2BSPVERSION)
4557 Mod_Q2BSP_Load(mod,buffer);
4559 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4562 void Mod_MAP_Load(model_t *mod, void *buffer)
4564 Host_Error("Mod_MAP_Load: not yet implemented\n");