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 strcpy(litfilename, loadmodel->name);
1042 FS_StripExtension(litfilename, litfilename);
1043 strcat(litfilename, ".lit");
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 strcpy(lightsfilename, loadmodel->name);
1097 FS_StripExtension(lightsfilename, lightsfilename);
1098 strcat(lightsfilename, ".lights");
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 strncpy(out->name, in->name, sizeof(out->name) - 1);
3319 out->surfaceflags = LittleLong(in->surfaceflags);
3320 out->nativecontents = LittleLong(in->contents);
3321 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3322 out->renderflags = 0;
3323 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3324 out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
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 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
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)
3540 in = (void *)(mod_base + l->fileofs);
3541 if (l->filelen % sizeof(*in))
3542 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3543 count = l->filelen / sizeof(*in);
3544 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3546 loadmodel->brushq3.data_lightmaps = out;
3547 loadmodel->brushq3.num_lightmaps = count;
3549 for (i = 0;i < count;i++, in++, out++)
3550 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3553 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3557 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3558 //int *originalelement3i;
3559 //int *originalneighbor3i;
3560 float *originalvertex3f;
3561 //float *originalsvector3f;
3562 //float *originaltvector3f;
3563 //float *originalnormal3f;
3564 float *originalcolor4f;
3565 float *originaltexcoordtexture2f;
3566 float *originaltexcoordlightmap2f;
3569 in = (void *)(mod_base + l->fileofs);
3570 if (l->filelen % sizeof(*in))
3571 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3572 count = l->filelen / sizeof(*in);
3573 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3575 loadmodel->brushq3.data_faces = out;
3576 loadmodel->brushq3.num_faces = count;
3578 for (i = 0;i < count;i++, in++, out++)
3580 // check face type first
3581 out->type = LittleLong(in->type);
3582 if (out->type != Q3FACETYPE_POLYGON
3583 && out->type != Q3FACETYPE_PATCH
3584 && out->type != Q3FACETYPE_MESH
3585 && out->type != Q3FACETYPE_FLARE)
3587 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3588 out->type = 0; // error
3592 n = LittleLong(in->textureindex);
3593 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3595 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3596 out->type = 0; // error
3600 out->texture = loadmodel->brushq3.data_textures + n;
3601 n = LittleLong(in->effectindex);
3602 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3604 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3610 out->effect = loadmodel->brushq3.data_effects + n;
3611 n = LittleLong(in->lightmapindex);
3612 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3614 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3618 out->lightmaptexture = NULL;
3620 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3622 out->firstvertex = LittleLong(in->firstvertex);
3623 out->num_vertices = LittleLong(in->numvertices);
3624 out->firstelement = LittleLong(in->firstelement);
3625 out->num_triangles = LittleLong(in->numelements) / 3;
3626 if (out->num_triangles * 3 != LittleLong(in->numelements))
3628 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));
3629 out->type = 0; // error
3632 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3634 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);
3635 out->type = 0; // error
3638 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3640 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);
3641 out->type = 0; // error
3644 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3645 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3646 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3647 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3648 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3649 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3650 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3651 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3652 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3655 case Q3FACETYPE_POLYGON:
3656 case Q3FACETYPE_MESH:
3657 // no processing necessary
3659 case Q3FACETYPE_PATCH:
3660 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3661 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3662 if (patchsize[0] < 1 || patchsize[1] < 1)
3664 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3665 out->type = 0; // error
3668 // convert patch to Q3FACETYPE_MESH
3669 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3670 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3671 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3672 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3673 finalvertices = finalwidth * finalheight;
3674 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3675 originalvertex3f = out->data_vertex3f;
3676 //originalsvector3f = out->data_svector3f;
3677 //originaltvector3f = out->data_tvector3f;
3678 //originalnormal3f = out->data_normal3f;
3679 originalcolor4f = out->data_color4f;
3680 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3681 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3682 //originalelement3i = out->data_element3i;
3683 //originalneighbor3i = out->data_neighbor3i;
3684 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
3685 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3686 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3687 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3688 out->data_color4f = out->data_normal3f + finalvertices * 3;
3689 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3690 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3691 out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
3692 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3693 out->type = Q3FACETYPE_MESH;
3694 out->firstvertex = -1;
3695 out->num_vertices = finalvertices;
3696 out->firstelement = -1;
3697 out->num_triangles = finaltriangles;
3698 // generate geometry
3699 // (note: normals are skipped because they get recalculated)
3700 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3701 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3702 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3703 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3704 // generate elements
3705 e = out->data_element3i;
3706 for (y = 0;y < finalheight - 1;y++)
3708 row0 = (y + 0) * finalwidth;
3709 row1 = (y + 1) * finalwidth;
3710 for (x = 0;x < finalwidth - 1;x++)
3722 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3723 if (developer.integer)
3725 if (out->num_triangles < finaltriangles)
3726 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_vertices, finaltriangles, finaltriangles - out->num_triangles, out->num_triangles);
3728 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);
3730 // q3map does not put in collision brushes for curves... ugh
3731 out->collisions = true;
3733 case Q3FACETYPE_FLARE:
3734 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3736 out->num_triangles = 0;
3739 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
3740 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3742 if (invalidelements)
3744 Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->num_vertices, out->firstelement, out->num_triangles * 3);
3745 for (j = 0;j < out->num_triangles * 3;j++)
3747 Con_Printf(" %i", out->data_element3i[j]);
3748 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3749 out->data_element3i[j] = 0;
3753 // for shadow volumes
3754 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
3755 // for per pixel lighting
3756 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);
3757 // calculate a bounding box
3758 VectorClear(out->mins);
3759 VectorClear(out->maxs);
3760 if (out->num_vertices)
3762 VectorCopy(out->data_vertex3f, out->mins);
3763 VectorCopy(out->data_vertex3f, out->maxs);
3764 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
3766 out->mins[0] = min(out->mins[0], v[0]);
3767 out->maxs[0] = max(out->maxs[0], v[0]);
3768 out->mins[1] = min(out->mins[1], v[1]);
3769 out->maxs[1] = max(out->maxs[1], v[1]);
3770 out->mins[2] = min(out->mins[2], v[2]);
3771 out->maxs[2] = max(out->maxs[2], v[2]);
3773 out->mins[0] -= 1.0f;
3774 out->mins[1] -= 1.0f;
3775 out->mins[2] -= 1.0f;
3776 out->maxs[0] += 1.0f;
3777 out->maxs[1] += 1.0f;
3778 out->maxs[2] += 1.0f;
3783 static void Mod_Q3BSP_LoadModels(lump_t *l)
3787 int i, j, n, c, count;
3789 in = (void *)(mod_base + l->fileofs);
3790 if (l->filelen % sizeof(*in))
3791 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3792 count = l->filelen / sizeof(*in);
3793 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3795 loadmodel->brushq3.data_models = out;
3796 loadmodel->brushq3.num_models = count;
3798 for (i = 0;i < count;i++, in++, out++)
3800 for (j = 0;j < 3;j++)
3802 out->mins[j] = LittleFloat(in->mins[j]);
3803 out->maxs[j] = LittleFloat(in->maxs[j]);
3805 n = LittleLong(in->firstface);
3806 c = LittleLong(in->numfaces);
3807 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3808 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3809 out->firstface = loadmodel->brushq3.data_faces + n;
3811 n = LittleLong(in->firstbrush);
3812 c = LittleLong(in->numbrushes);
3813 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3814 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3815 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3816 out->numbrushes = c;
3820 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3826 in = (void *)(mod_base + l->fileofs);
3827 if (l->filelen % sizeof(*in))
3828 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3829 count = l->filelen / sizeof(*in);
3830 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3832 loadmodel->brushq3.data_leafbrushes = out;
3833 loadmodel->brushq3.num_leafbrushes = count;
3835 for (i = 0;i < count;i++, in++, out++)
3837 n = LittleLong(*in);
3838 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3839 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3840 *out = loadmodel->brushq3.data_brushes + n;
3844 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3850 in = (void *)(mod_base + l->fileofs);
3851 if (l->filelen % sizeof(*in))
3852 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3853 count = l->filelen / sizeof(*in);
3854 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3856 loadmodel->brushq3.data_leaffaces = out;
3857 loadmodel->brushq3.num_leaffaces = count;
3859 for (i = 0;i < count;i++, in++, out++)
3861 n = LittleLong(*in);
3862 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3863 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3864 *out = loadmodel->brushq3.data_faces + n;
3868 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3872 int i, j, n, c, count;
3874 in = (void *)(mod_base + l->fileofs);
3875 if (l->filelen % sizeof(*in))
3876 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3877 count = l->filelen / sizeof(*in);
3878 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3880 loadmodel->brushq3.data_leafs = out;
3881 loadmodel->brushq3.num_leafs = count;
3883 for (i = 0;i < count;i++, in++, out++)
3885 out->isnode = false;
3887 out->clusterindex = LittleLong(in->clusterindex);
3888 out->areaindex = LittleLong(in->areaindex);
3889 for (j = 0;j < 3;j++)
3891 // yes the mins/maxs are ints
3892 out->mins[j] = LittleLong(in->mins[j]);
3893 out->maxs[j] = LittleLong(in->maxs[j]);
3895 n = LittleLong(in->firstleafface);
3896 c = LittleLong(in->numleaffaces);
3897 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3898 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3899 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3900 out->numleaffaces = c;
3901 n = LittleLong(in->firstleafbrush);
3902 c = LittleLong(in->numleafbrushes);
3903 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3904 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3905 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3906 out->numleafbrushes = c;
3910 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3913 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3914 node->parent = parent;
3917 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3918 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3922 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3928 in = (void *)(mod_base + l->fileofs);
3929 if (l->filelen % sizeof(*in))
3930 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3931 count = l->filelen / sizeof(*in);
3932 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3934 loadmodel->brushq3.data_nodes = out;
3935 loadmodel->brushq3.num_nodes = count;
3937 for (i = 0;i < count;i++, in++, out++)
3941 n = LittleLong(in->planeindex);
3942 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3943 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3944 out->plane = loadmodel->brushq3.data_planes + n;
3945 for (j = 0;j < 2;j++)
3947 n = LittleLong(in->childrenindex[j]);
3950 if (n >= loadmodel->brushq3.num_nodes)
3951 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3952 out->children[j] = loadmodel->brushq3.data_nodes + n;
3957 if (n >= loadmodel->brushq3.num_leafs)
3958 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3959 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3962 for (j = 0;j < 3;j++)
3964 // yes the mins/maxs are ints
3965 out->mins[j] = LittleLong(in->mins[j]);
3966 out->maxs[j] = LittleLong(in->maxs[j]);
3970 // set the parent pointers
3971 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3974 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3977 q3dlightgrid_t *out;
3980 if (l->filelen == 0)
3983 in = (void *)(mod_base + l->fileofs);
3984 if (l->filelen % sizeof(*in))
3985 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
3986 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
3987 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
3988 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
3989 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3990 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3991 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3992 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3993 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3994 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3995 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
3996 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
3997 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
3998 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
3999 if (l->filelen < count * (int)sizeof(*in))
4000 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]);
4001 if (l->filelen != count * (int)sizeof(*in))
4002 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4004 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4005 loadmodel->brushq3.data_lightgrid = out;
4006 loadmodel->brushq3.num_lightgrid = count;
4008 // no swapping or validation necessary
4009 memcpy(out, in, count * (int)sizeof(*out));
4011 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]);
4012 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]);
4015 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4020 if (l->filelen == 0)
4023 in = (void *)(mod_base + l->fileofs);
4025 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4027 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4028 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4029 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4030 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4031 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4032 if (l->filelen < totalchains + (int)sizeof(*in))
4033 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);
4035 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4036 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4039 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4041 // FIXME: finish this code
4042 VectorCopy(in, out);
4045 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4047 int i, j, k, index[3];
4048 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4049 q3dlightgrid_t *a, *s;
4050 // FIXME: write this
4051 if (!model->brushq3.num_lightgrid)
4053 ambientcolor[0] += 128;
4054 ambientcolor[1] += 128;
4055 ambientcolor[2] += 128;
4058 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4059 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4060 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4061 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0]);
4062 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1]);
4063 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2]);
4064 index[0] = (int)floor(transformed[0]);
4065 index[1] = (int)floor(transformed[1]);
4066 index[2] = (int)floor(transformed[2]);
4067 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4068 // now lerp the values
4069 VectorClear(diffusenormal);
4070 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4071 for (k = 0;k < 2;k++)
4073 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4074 for (j = 0;j < 2;j++)
4076 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4077 for (i = 0;i < 2;i++)
4079 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4080 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4081 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4082 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4083 pitch = s->diffusepitch * M_PI / 128;
4084 yaw = s->diffuseyaw * M_PI / 128;
4085 sinpitch = sin(pitch);
4086 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4087 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4088 diffusenormal[2] += blend * (cos(pitch));
4089 //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)));
4093 VectorNormalize(diffusenormal);
4094 //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]);
4097 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)
4099 int i, startside, endside;
4100 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4104 if (startfrac >= trace->fraction)
4106 // note: all line fragments past first impact fraction are ignored
4107 while (node->isnode)
4109 // recurse down node sides
4110 dist1 = PlaneDiff(start, node->plane);
4111 dist2 = PlaneDiff(end, node->plane);
4112 startside = dist1 < 0;
4113 endside = dist2 < 0;
4114 if (startside == endside)
4116 // most of the time the line fragment is on one side of the plane
4117 node = node->children[startside];
4121 // line crosses node plane, split the line
4122 midfrac = dist1 / (dist1 - dist2);
4123 VectorLerp(linestart, midfrac, lineend, mid);
4124 // take the near side first
4125 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4126 if (midfrac < trace->fraction)
4127 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4132 segmentmins[0] = min(start[0], end[0]);
4133 segmentmins[1] = min(start[1], end[1]);
4134 segmentmins[2] = min(start[2], end[2]);
4135 segmentmaxs[0] = max(start[0], end[0]);
4136 segmentmaxs[1] = max(start[1], end[1]);
4137 segmentmaxs[2] = max(start[2], end[2]);
4138 leaf = (q3mleaf_t *)node;
4139 for (i = 0;i < leaf->numleafbrushes;i++)
4141 if (startfrac >= trace->fraction)
4143 brush = leaf->firstleafbrush[i]->colbrushf;
4144 if (brush && brush->markframe != markframe)
4146 brush->markframe = markframe;
4147 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4148 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4151 if (mod_q3bsp_curves_collisions.integer)
4153 for (i = 0;i < leaf->numleaffaces;i++)
4155 if (startfrac >= trace->fraction)
4157 face = leaf->firstleafface[i];
4158 if (face->collisions && face->collisionmarkframe != markframe)
4160 face->collisionmarkframe = markframe;
4161 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4162 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4168 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)
4171 float nodesegmentmins[3], nodesegmentmaxs[3];
4175 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4176 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4177 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4178 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4179 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4180 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4181 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4185 // recurse down node sides
4186 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4189 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4190 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4192 else if (sides == 2)
4193 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4195 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4197 dist = node->plane->dist - (1.0f / 8.0f);
4198 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4200 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4202 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4208 dist = node->plane->dist + (1.0f / 8.0f);
4209 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4211 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4213 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4219 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4221 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4223 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4229 leaf = (q3mleaf_t *)node;
4230 for (i = 0;i < leaf->numleafbrushes;i++)
4232 brush = leaf->firstleafbrush[i]->colbrushf;
4233 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4235 brush->markframe = markframe;
4236 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4239 if (mod_q3bsp_curves_collisions.integer)
4241 for (i = 0;i < leaf->numleaffaces;i++)
4243 face = leaf->firstleafface[i];
4244 // 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
4245 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4246 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4252 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)
4255 float segmentmins[3], segmentmaxs[3];
4256 colbrushf_t *thisbrush_start, *thisbrush_end;
4257 matrix4x4_t startmatrix, endmatrix;
4258 static int markframe = 0;
4260 memset(trace, 0, sizeof(*trace));
4261 trace->fraction = 1;
4262 trace->hitsupercontentsmask = hitsupercontentsmask;
4263 Matrix4x4_CreateIdentity(&startmatrix);
4264 Matrix4x4_CreateIdentity(&endmatrix);
4265 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4266 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4267 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4268 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4269 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4270 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4271 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4274 if (model->brushq3.submodel)
4276 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4277 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4278 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4279 if (mod_q3bsp_curves_collisions.integer)
4281 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4283 face = model->brushq3.data_thismodel->firstface + i;
4284 if (face->collisions)
4285 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4290 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4294 // box trace, performed as brush trace
4295 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4296 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4297 if (model->brushq3.submodel)
4299 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4300 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4301 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4302 if (mod_q3bsp_curves_collisions.integer)
4304 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4306 face = model->brushq3.data_thismodel->firstface + i;
4307 if (face->collisions)
4308 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4313 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4318 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)
4325 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4326 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4329 // node - recurse down the BSP tree
4330 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4333 node = node->children[0];
4336 node = node->children[1];
4338 default: // crossing
4339 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4341 node = node->children[1];
4348 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4350 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4353 //Returns PVS data for a given point
4354 //(note: can return NULL)
4355 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4358 Mod_CheckLoaded(model);
4359 node = model->brushq3.data_nodes;
4360 while (node->isnode)
4361 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4362 if (((q3mleaf_t *)node)->clusterindex >= 0)
4363 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4368 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4374 while (node->isnode)
4376 d = PlaneDiff(org, node->plane);
4378 node = node->children[0];
4379 else if (d < -radius)
4380 node = node->children[1];
4383 // go down both sides
4384 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4385 node = node->children[1];
4388 // if this is a leaf with a pvs, accumulate the pvs bits
4389 if (((q3mleaf_t *)node)->clusterindex >= 0)
4391 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4392 for (i = 0;i < pvsbytes;i++)
4393 pvsbuffer[i] |= pvs[i];
4398 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4399 //of the given point.
4400 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4402 int bytes = model->brushq3.num_pvschainlength;
4403 bytes = min(bytes, pvsbufferlength);
4404 memset(pvsbuffer, 0, bytes);
4405 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4410 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4412 int supercontents = 0;
4413 if (nativecontents & Q2CONTENTS_SOLID)
4414 supercontents |= SUPERCONTENTS_SOLID;
4415 if (nativecontents & Q2CONTENTS_WATER)
4416 supercontents |= SUPERCONTENTS_WATER;
4417 if (nativecontents & Q2CONTENTS_SLIME)
4418 supercontents |= SUPERCONTENTS_SLIME;
4419 if (nativecontents & Q2CONTENTS_LAVA)
4420 supercontents |= SUPERCONTENTS_LAVA;
4421 return supercontents;
4424 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4426 int nativecontents = 0;
4427 if (supercontents & SUPERCONTENTS_SOLID)
4428 nativecontents |= Q2CONTENTS_SOLID;
4429 if (supercontents & SUPERCONTENTS_WATER)
4430 nativecontents |= Q2CONTENTS_WATER;
4431 if (supercontents & SUPERCONTENTS_SLIME)
4432 nativecontents |= Q2CONTENTS_SLIME;
4433 if (supercontents & SUPERCONTENTS_LAVA)
4434 nativecontents |= Q2CONTENTS_LAVA;
4435 return nativecontents;
4438 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4439 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4440 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4441 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);
4442 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4445 q3dheader_t *header;
4446 float corner[3], yawradius, modelradius;
4448 mod->type = mod_brushq3;
4452 header = (q3dheader_t *)buffer;
4454 i = LittleLong(header->version);
4455 if (i != Q3BSPVERSION)
4456 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4457 if (loadmodel->isworldmodel)
4459 Cvar_SetValue("halflifebsp", false);
4460 // until we get a texture for it...
4464 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4465 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4466 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4467 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4468 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4469 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4470 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4471 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4472 //mod->DrawSky = R_Q3BSP_DrawSky;
4473 mod->Draw = R_Q3BSP_Draw;
4474 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4475 mod->DrawLight = R_Q3BSP_DrawLight;
4477 mod_base = (qbyte *)header;
4479 // swap all the lumps
4480 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4481 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4483 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4484 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4485 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4486 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4487 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4488 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4489 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4490 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4491 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4492 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4493 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4494 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4495 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4496 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4497 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4498 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4499 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4500 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4502 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4509 // LordHavoc: only register submodels if it is the world
4510 // (prevents bsp models from replacing world submodels)
4511 if (!loadmodel->isworldmodel)
4513 // duplicate the basic information
4514 sprintf(name, "*%i", i);
4515 mod = Mod_FindName(name);
4517 strcpy(mod->name, name);
4518 // textures and memory belong to the main model
4519 mod->texturepool = NULL;
4520 mod->mempool = NULL;
4522 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4523 mod->brushq3.submodel = i;
4525 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4526 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4527 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4528 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4529 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4530 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4531 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4532 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4533 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4534 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4535 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4536 mod->yawmins[2] = mod->normalmins[2];
4537 mod->yawmaxs[2] = mod->normalmaxs[2];
4538 mod->radius = modelradius;
4539 mod->radius2 = modelradius * modelradius;
4543 void Mod_IBSP_Load(model_t *mod, void *buffer)
4545 int i = LittleLong(((int *)buffer)[1]);
4546 if (i == Q3BSPVERSION)
4547 Mod_Q3BSP_Load(mod,buffer);
4548 else if (i == Q2BSPVERSION)
4549 Mod_Q2BSP_Load(mod,buffer);
4551 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4554 void Mod_MAP_Load(model_t *mod, void *buffer)
4556 Host_Error("Mod_MAP_Load: not yet implemented\n");