2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
38 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
39 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
41 void Mod_BrushInit(void)
43 // Cvar_RegisterVariable(&r_subdivide_size);
44 Cvar_RegisterVariable(&halflifebsp);
45 Cvar_RegisterVariable(&r_novis);
46 Cvar_RegisterVariable(&r_miplightmaps);
47 Cvar_RegisterVariable(&r_lightmaprgba);
48 Cvar_RegisterVariable(&r_nosurftextures);
49 Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
50 Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
51 Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
52 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
62 Mod_CheckLoaded(model);
64 // LordHavoc: modified to start at first clip node,
65 // in other words: first node of the (sub)model
66 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
67 while (node->contents == 0)
68 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
70 return (mleaf_t *)node;
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
77 leaf = Mod_Q1BSP_PointInLeaf(model, p);
80 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
83 memcpy(out, leaf->ambient_sound_level, i);
89 memset(out, 0, outsize);
93 static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
97 if (node->contents < 0)
100 if (node->contents == CONTENTS_SOLID)
102 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
106 // node - recurse down the BSP tree
107 switch (BoxOnPlaneSide(mins, maxs, node->plane))
110 node = node->children[0];
113 node = node->children[1];
116 if (node->children[0]->contents != CONTENTS_SOLID)
117 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
119 node = node->children[1];
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
128 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
137 return CONTENTS_EMPTY;
139 Mod_CheckLoaded(model);
141 // LordHavoc: modified to start at first clip node,
142 // in other words: first node of the (sub)model
143 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
144 while (node->contents == 0)
145 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
147 return ((mleaf_t *)node)->contents;
151 typedef struct findnonsolidlocationinfo_s
159 findnonsolidlocationinfo_t;
162 extern cvar_t samelevel;
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
166 int i, surfnum, k, *tri, *mark;
167 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
172 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
174 surf = info->model->brushq1.surfaces + *mark;
175 if (surf->flags & SURF_SOLIDCLIP)
178 VectorCopy(surf->plane->normal, surfnormal);
179 if (surf->flags & SURF_PLANEBACK)
180 VectorNegate(surfnormal, surfnormal);
182 for (k = 0;k < surf->mesh.num_triangles;k++)
184 tri = surf->mesh.data_element3i + k * 3;
185 VectorCopy((surf->mesh.data_vertex3f + tri[0] * 3), vert[0]);
186 VectorCopy((surf->mesh.data_vertex3f + tri[1] * 3), vert[1]);
187 VectorCopy((surf->mesh.data_vertex3f + tri[2] * 3), vert[2]);
188 VectorSubtract(vert[1], vert[0], edge[0]);
189 VectorSubtract(vert[2], vert[1], edge[1]);
190 CrossProduct(edge[1], edge[0], facenormal);
191 if (facenormal[0] || facenormal[1] || facenormal[2])
193 VectorNormalize(facenormal);
195 if (VectorDistance(facenormal, surfnormal) > 0.01f)
196 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
198 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
199 if (f <= info->bestdist && f >= -info->bestdist)
201 VectorSubtract(vert[0], vert[2], edge[2]);
202 VectorNormalize(edge[0]);
203 VectorNormalize(edge[1]);
204 VectorNormalize(edge[2]);
205 CrossProduct(facenormal, edge[0], edgenormal[0]);
206 CrossProduct(facenormal, edge[1], edgenormal[1]);
207 CrossProduct(facenormal, edge[2], edgenormal[2]);
209 if (samelevel.integer & 1)
210 VectorNegate(edgenormal[0], edgenormal[0]);
211 if (samelevel.integer & 2)
212 VectorNegate(edgenormal[1], edgenormal[1]);
213 if (samelevel.integer & 4)
214 VectorNegate(edgenormal[2], edgenormal[2]);
215 for (i = 0;i < 3;i++)
216 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
217 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
218 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
219 Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]);
222 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
223 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
224 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
226 // we got lucky, the center is within the face
227 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
231 if (info->bestdist > dist)
233 info->bestdist = dist;
234 VectorScale(facenormal, (info->radius - -dist), info->nudge);
239 if (info->bestdist > dist)
241 info->bestdist = dist;
242 VectorScale(facenormal, (info->radius - dist), info->nudge);
248 // check which edge or vertex the center is nearest
249 for (i = 0;i < 3;i++)
251 f = DotProduct(info->center, edge[i]);
252 if (f >= DotProduct(vert[0], edge[i])
253 && f <= DotProduct(vert[1], edge[i]))
256 VectorMA(info->center, -f, edge[i], point);
257 dist = sqrt(DotProduct(point, point));
258 if (info->bestdist > dist)
260 info->bestdist = dist;
261 VectorScale(point, (info->radius / dist), info->nudge);
263 // skip both vertex checks
264 // (both are further away than this edge)
269 // not on edge, check first vertex of edge
270 VectorSubtract(info->center, vert[i], point);
271 dist = sqrt(DotProduct(point, point));
272 if (info->bestdist > dist)
274 info->bestdist = dist;
275 VectorScale(point, (info->radius / dist), info->nudge);
287 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
291 if (((mleaf_t *)node)->nummarksurfaces)
292 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
296 float f = PlaneDiff(info->center, node->plane);
297 if (f >= -info->bestdist)
298 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
299 if (f <= info->bestdist)
300 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
304 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
307 findnonsolidlocationinfo_t info;
313 VectorCopy(in, info.center);
314 info.radius = radius;
319 VectorClear(info.nudge);
320 info.bestdist = radius;
321 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
322 VectorAdd(info.center, info.nudge, info.center);
324 while (info.bestdist < radius && ++i < 10);
325 VectorCopy(info.center, out);
328 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
330 switch(nativecontents)
335 return SUPERCONTENTS_SOLID;
337 return SUPERCONTENTS_WATER;
339 return SUPERCONTENTS_SLIME;
341 return SUPERCONTENTS_LAVA;
343 return SUPERCONTENTS_SKY;
348 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
350 if (supercontents & SUPERCONTENTS_SOLID)
351 return CONTENTS_SOLID;
352 if (supercontents & SUPERCONTENTS_SKY)
354 if (supercontents & SUPERCONTENTS_LAVA)
355 return CONTENTS_LAVA;
356 if (supercontents & SUPERCONTENTS_SLIME)
357 return CONTENTS_SLIME;
358 if (supercontents & SUPERCONTENTS_WATER)
359 return CONTENTS_WATER;
360 return CONTENTS_EMPTY;
365 // the hull we're tracing through
368 // the trace structure to fill in
371 // start, end, and end - start (in model space)
376 RecursiveHullCheckTraceInfo_t;
378 // 1/32 epsilon to keep floating point happy
379 #define DIST_EPSILON (0.03125)
381 #define HULLCHECKSTATE_EMPTY 0
382 #define HULLCHECKSTATE_SOLID 1
383 #define HULLCHECKSTATE_DONE 2
385 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
387 // status variables, these don't need to be saved on the stack when
388 // recursing... but are because this should be thread-safe
389 // (note: tracing against a bbox is not thread-safe, yet)
394 // variables that need to be stored on the stack when recursing
399 // LordHavoc: a goto! everyone flee in terror... :)
404 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
405 if (!t->trace->startfound)
407 t->trace->startfound = true;
408 t->trace->startsupercontents |= num;
410 if (num & SUPERCONTENTS_LIQUIDSMASK)
411 t->trace->inwater = true;
413 t->trace->inopen = true;
414 if (num & t->trace->hitsupercontentsmask)
416 // if the first leaf is solid, set startsolid
417 if (t->trace->allsolid)
418 t->trace->startsolid = true;
419 return HULLCHECKSTATE_SOLID;
423 t->trace->allsolid = false;
424 return HULLCHECKSTATE_EMPTY;
428 // find the point distances
429 node = t->hull->clipnodes + num;
431 plane = t->hull->planes + node->planenum;
434 t1 = p1[plane->type] - plane->dist;
435 t2 = p2[plane->type] - plane->dist;
439 t1 = DotProduct (plane->normal, p1) - plane->dist;
440 t2 = DotProduct (plane->normal, p2) - plane->dist;
447 num = node->children[1];
456 num = node->children[0];
462 // the line intersects, find intersection point
463 // LordHavoc: this uses the original trace for maximum accuracy
466 t1 = t->start[plane->type] - plane->dist;
467 t2 = t->end[plane->type] - plane->dist;
471 t1 = DotProduct (plane->normal, t->start) - plane->dist;
472 t2 = DotProduct (plane->normal, t->end) - plane->dist;
475 midf = t1 / (t1 - t2);
476 midf = bound(p1f, midf, p2f);
477 VectorMA(t->start, midf, t->dist, mid);
479 // recurse both sides, front side first
480 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
481 // if this side is not empty, return what it is (solid or done)
482 if (ret != HULLCHECKSTATE_EMPTY)
485 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
486 // if other side is not solid, return what it is (empty or done)
487 if (ret != HULLCHECKSTATE_SOLID)
490 // front is air and back is solid, this is the impact point...
493 t->trace->plane.dist = -plane->dist;
494 VectorNegate (plane->normal, t->trace->plane.normal);
498 t->trace->plane.dist = plane->dist;
499 VectorCopy (plane->normal, t->trace->plane.normal);
502 // bias away from surface a bit
503 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
504 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
506 midf = t1 / (t1 - t2);
507 t->trace->fraction = bound(0.0f, midf, 1.0);
509 return HULLCHECKSTATE_DONE;
512 static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
514 // this function currently only supports same size start and end
516 RecursiveHullCheckTraceInfo_t rhc;
518 memset(&rhc, 0, sizeof(rhc));
519 memset(trace, 0, sizeof(trace_t));
521 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
522 rhc.trace->fraction = 1;
523 rhc.trace->allsolid = true;
524 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
526 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
527 else if (model->brush.ishlbsp)
529 if (boxsize[0] <= 32)
531 if (boxsize[2] < 54) // pick the nearest of 36 or 72
532 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
534 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
537 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
541 if (boxsize[0] <= 32)
542 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
544 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
546 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
547 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
548 VectorSubtract(rhc.end, rhc.start, rhc.dist);
549 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
552 static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
554 int side, distz = endz - startz;
559 if (node->contents < 0)
560 return false; // didn't hit anything
562 switch (node->plane->type)
565 node = node->children[x < node->plane->dist];
568 node = node->children[y < node->plane->dist];
571 side = startz < node->plane->dist;
572 if ((endz < node->plane->dist) == side)
574 node = node->children[side];
577 // found an intersection
578 mid = node->plane->dist;
581 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
582 front += startz * node->plane->normal[2];
583 back += endz * node->plane->normal[2];
584 side = front < node->plane->dist;
585 if ((back < node->plane->dist) == side)
587 node = node->children[side];
590 // found an intersection
591 mid = startz + distz * (front - node->plane->dist) / (front - back);
595 // go down front side
596 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
597 return true; // hit something
600 // check for impact on this node
601 if (node->numsurfaces)
606 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
607 for (i = 0;i < node->numsurfaces;i++, surf++)
609 if (!(surf->flags & SURF_LIGHTMAP))
610 continue; // no lightmaps
612 ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3]);
613 dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]);
615 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
618 ds -= surf->texturemins[0];
619 dt -= surf->texturemins[1];
621 if (ds > surf->extents[0] || dt > surf->extents[1])
627 int maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
628 line3 = ((surf->extents[0]>>4)+1)*3;
629 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
631 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
633 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
635 scale = d_lightstylevalue[surf->styles[maps]];
636 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
637 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
638 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
639 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
644 LordHavoc: here's the readable version of the interpolation
645 code, not quite as easy for the compiler to optimize...
647 dsfrac is the X position in the lightmap pixel, * 16
648 dtfrac is the Y position in the lightmap pixel, * 16
649 r00 is top left corner, r01 is top right corner
650 r10 is bottom left corner, r11 is bottom right corner
651 g and b are the same layout.
652 r0 and r1 are the top and bottom intermediate results
654 first we interpolate the top two points, to get the top
657 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
658 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
659 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
661 then we interpolate the bottom two points, to get the
664 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
665 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
666 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
668 then we interpolate the top and bottom samples to get the
669 middle sample (the one which was requested)
671 r = (((r1-r0) * dtfrac) >> 4) + r0;
672 g = (((g1-g0) * dtfrac) >> 4) + g0;
673 b = (((b1-b0) * dtfrac) >> 4) + b0;
676 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
677 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
678 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
680 return true; // success
685 node = node->children[side ^ 1];
687 distz = endz - startz;
692 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
694 Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, cl.worldmodel->brushq1.nodes + cl.worldmodel->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
697 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
704 Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
712 for (c = *in++;c > 0;c--)
716 Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
725 static void Mod_Q1BSP_LoadTextures(lump_t *l)
727 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
729 texture_t *tx, *tx2, *anims[10], *altanims[10];
731 qbyte *data, *mtdata;
734 loadmodel->brushq1.textures = NULL;
736 // add two slots for notexture walls and notexture liquids
739 m = (dmiptexlump_t *)(mod_base + l->fileofs);
740 m->nummiptex = LittleLong (m->nummiptex);
741 loadmodel->brushq1.numtextures = m->nummiptex + 2;
746 loadmodel->brushq1.numtextures = 2;
749 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
751 // fill out all slots with notexture
752 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
755 strcpy(tx->name, "NO TEXTURE FOUND");
758 tx->skin.base = r_notexture;
759 tx->shader = &Cshader_wall_lightmap;
760 tx->flags = SURF_SOLIDCLIP;
761 if (i == loadmodel->brushq1.numtextures - 1)
763 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
764 tx->shader = &Cshader_water;
766 tx->currentframe = tx;
772 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
774 // LordHavoc: mostly rewritten map texture loader
775 for (i = 0;i < m->nummiptex;i++)
777 dofs[i] = LittleLong(dofs[i]);
778 if (dofs[i] == -1 || r_nosurftextures.integer)
780 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
782 // make sure name is no more than 15 characters
783 for (j = 0;dmiptex->name[j] && j < 15;j++)
784 name[j] = dmiptex->name[j];
787 mtwidth = LittleLong(dmiptex->width);
788 mtheight = LittleLong(dmiptex->height);
790 j = LittleLong(dmiptex->offsets[0]);
794 if (j < 40 || j + mtwidth * mtheight > l->filelen)
796 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
799 mtdata = (qbyte *)dmiptex + j;
802 if ((mtwidth & 15) || (mtheight & 15))
803 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
805 // LordHavoc: force all names to lowercase
806 for (j = 0;name[j];j++)
807 if (name[j] >= 'A' && name[j] <= 'Z')
808 name[j] += 'a' - 'A';
810 tx = loadmodel->brushq1.textures + i;
811 strcpy(tx->name, name);
813 tx->height = mtheight;
817 sprintf(tx->name, "unnamed%i", i);
818 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
821 // LordHavoc: HL sky textures are entirely different than quake
822 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
824 if (loadmodel->isworldmodel)
826 data = loadimagepixels(tx->name, false, 0, 0);
829 if (image_width == 256 && image_height == 128)
837 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
839 R_InitSky(mtdata, 1);
842 else if (mtdata != NULL)
843 R_InitSky(mtdata, 1);
848 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
850 // did not find external texture, load it from the bsp or wad3
851 if (loadmodel->brush.ishlbsp)
853 // internal texture overrides wad
854 qbyte *pixels, *freepixels, *fogpixels;
855 pixels = freepixels = NULL;
857 pixels = W_ConvertWAD3Texture(dmiptex);
859 pixels = freepixels = W_GetTexture(tx->name);
862 tx->width = image_width;
863 tx->height = image_height;
864 tx->skin.base = tx->skin.merged = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
865 if (Image_CheckAlpha(pixels, image_width * image_height, true))
867 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
868 for (j = 0;j < image_width * image_height * 4;j += 4)
870 fogpixels[j + 0] = 255;
871 fogpixels[j + 1] = 255;
872 fogpixels[j + 2] = 255;
873 fogpixels[j + 3] = pixels[j + 3];
875 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
880 Mem_Free(freepixels);
882 else if (mtdata) // texture included
883 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
886 if (tx->skin.base == NULL)
891 tx->skin.base = r_notexture;
894 if (tx->name[0] == '*')
896 // turb does not block movement
897 tx->flags &= ~SURF_SOLIDCLIP;
898 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
899 // LordHavoc: some turbulent textures should be fullbright and solid
900 if (!strncmp(tx->name,"*lava",5)
901 || !strncmp(tx->name,"*teleport",9)
902 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
903 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
905 tx->flags |= SURF_WATERALPHA;
906 tx->shader = &Cshader_water;
908 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
910 tx->flags |= SURF_DRAWSKY;
911 tx->shader = &Cshader_sky;
915 tx->flags |= SURF_LIGHTMAP;
917 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
918 tx->shader = &Cshader_wall_lightmap;
921 // start out with no animation
922 tx->currentframe = tx;
925 // sequence the animations
926 for (i = 0;i < m->nummiptex;i++)
928 tx = loadmodel->brushq1.textures + i;
929 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
931 if (tx->anim_total[0] || tx->anim_total[1])
932 continue; // already sequenced
934 // find the number of frames in the animation
935 memset(anims, 0, sizeof(anims));
936 memset(altanims, 0, sizeof(altanims));
938 for (j = i;j < m->nummiptex;j++)
940 tx2 = loadmodel->brushq1.textures + j;
941 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
945 if (num >= '0' && num <= '9')
946 anims[num - '0'] = tx2;
947 else if (num >= 'a' && num <= 'j')
948 altanims[num - 'a'] = tx2;
950 Con_Printf("Bad animating texture %s\n", tx->name);
954 for (j = 0;j < 10;j++)
961 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
964 for (j = 0;j < max;j++)
968 Con_Printf("Missing frame %i of %s\n", j, tx->name);
972 for (j = 0;j < altmax;j++)
976 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
985 // if there is no alternate animation, duplicate the primary
986 // animation into the alternate
988 for (k = 0;k < 10;k++)
989 altanims[k] = anims[k];
992 // link together the primary animation
993 for (j = 0;j < max;j++)
996 tx2->animated = true;
997 tx2->anim_total[0] = max;
998 tx2->anim_total[1] = altmax;
999 for (k = 0;k < 10;k++)
1001 tx2->anim_frames[0][k] = anims[k];
1002 tx2->anim_frames[1][k] = altanims[k];
1006 // if there really is an alternate anim...
1007 if (anims[0] != altanims[0])
1009 // link together the alternate animation
1010 for (j = 0;j < altmax;j++)
1013 tx2->animated = true;
1014 // the primary/alternate are reversed here
1015 tx2->anim_total[0] = altmax;
1016 tx2->anim_total[1] = max;
1017 for (k = 0;k < 10;k++)
1019 tx2->anim_frames[0][k] = altanims[k];
1020 tx2->anim_frames[1][k] = anims[k];
1027 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1030 qbyte *in, *out, *data, d;
1031 char litfilename[1024];
1032 loadmodel->brushq1.lightdata = NULL;
1033 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1035 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1036 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1038 else // LordHavoc: bsp version 29 (normal white lighting)
1040 // LordHavoc: hope is not lost yet, check for a .lit file to load
1041 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1042 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1043 strlcat (litfilename, ".lit", sizeof (litfilename));
1044 data = (qbyte*) FS_LoadFile(litfilename, false);
1047 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1049 i = LittleLong(((int *)data)[1]);
1052 Con_DPrintf("loaded %s\n", litfilename);
1053 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1054 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1060 Con_Printf("Unknown .lit file version (%d)\n", i);
1066 if (fs_filesize == 8)
1067 Con_Printf("Empty .lit file, ignoring\n");
1069 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1073 // LordHavoc: oh well, expand the white lighting data
1076 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1077 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1078 out = loadmodel->brushq1.lightdata;
1079 memcpy(in, mod_base + l->fileofs, l->filelen);
1080 for (i = 0;i < l->filelen;i++)
1090 static void Mod_Q1BSP_LoadLightList(void)
1092 int a, n, numlights;
1093 char lightsfilename[1024], *s, *t, *lightsstring;
1096 strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1097 FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1098 strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1099 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1105 while (*s && *s != '\n')
1109 Mem_Free(lightsstring);
1110 Host_Error("lights file must end with a newline\n");
1115 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1118 while (*s && n < numlights)
1121 while (*s && *s != '\n')
1125 Mem_Free(lightsstring);
1126 Host_Error("misparsed lights file!\n");
1128 e = loadmodel->brushq1.lights + n;
1130 a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
1134 Mem_Free(lightsstring);
1135 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
1142 Mem_Free(lightsstring);
1143 Host_Error("misparsed lights file!\n");
1145 loadmodel->brushq1.numlights = numlights;
1146 Mem_Free(lightsstring);
1150 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1152 loadmodel->brushq1.num_compressedpvs = 0;
1153 loadmodel->brushq1.data_compressedpvs = NULL;
1156 loadmodel->brushq1.num_compressedpvs = l->filelen;
1157 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1158 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1161 // used only for HalfLife maps
1162 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1164 char key[128], value[4096];
1169 if (!COM_ParseToken(&data, false))
1171 if (com_token[0] != '{')
1175 if (!COM_ParseToken(&data, false))
1177 if (com_token[0] == '}')
1178 break; // end of worldspawn
1179 if (com_token[0] == '_')
1180 strcpy(key, com_token + 1);
1182 strcpy(key, com_token);
1183 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1184 key[strlen(key)-1] = 0;
1185 if (!COM_ParseToken(&data, false))
1187 strcpy(value, com_token);
1188 if (!strcmp("wad", key)) // for HalfLife maps
1190 if (loadmodel->brush.ishlbsp)
1193 for (i = 0;i < 4096;i++)
1194 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1200 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1201 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1203 else if (value[i] == ';' || value[i] == 0)
1207 strcpy(wadname, "textures/");
1208 strcat(wadname, &value[j]);
1209 W_LoadTextureWadFile(wadname, false);
1221 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1223 loadmodel->brush.entities = NULL;
1226 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1227 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1228 if (loadmodel->brush.ishlbsp)
1229 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1233 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1239 in = (void *)(mod_base + l->fileofs);
1240 if (l->filelen % sizeof(*in))
1241 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1242 count = l->filelen / sizeof(*in);
1243 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1245 loadmodel->brushq1.vertexes = out;
1246 loadmodel->brushq1.numvertexes = count;
1248 for ( i=0 ; i<count ; i++, in++, out++)
1250 out->position[0] = LittleFloat(in->point[0]);
1251 out->position[1] = LittleFloat(in->point[1]);
1252 out->position[2] = LittleFloat(in->point[2]);
1256 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1262 in = (void *)(mod_base + l->fileofs);
1263 if (l->filelen % sizeof(*in))
1264 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1265 count = l->filelen / sizeof(*in);
1266 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1268 loadmodel->brushq1.submodels = out;
1269 loadmodel->brush.numsubmodels = count;
1271 for ( i=0 ; i<count ; i++, in++, out++)
1273 for (j=0 ; j<3 ; j++)
1275 // spread the mins / maxs by a pixel
1276 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1277 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1278 out->origin[j] = LittleFloat(in->origin[j]);
1280 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1281 out->headnode[j] = LittleLong(in->headnode[j]);
1282 out->visleafs = LittleLong(in->visleafs);
1283 out->firstface = LittleLong(in->firstface);
1284 out->numfaces = LittleLong(in->numfaces);
1288 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1294 in = (void *)(mod_base + l->fileofs);
1295 if (l->filelen % sizeof(*in))
1296 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1297 count = l->filelen / sizeof(*in);
1298 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1300 loadmodel->brushq1.edges = out;
1301 loadmodel->brushq1.numedges = count;
1303 for ( i=0 ; i<count ; i++, in++, out++)
1305 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1306 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1310 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1314 int i, j, k, count, miptex;
1316 in = (void *)(mod_base + l->fileofs);
1317 if (l->filelen % sizeof(*in))
1318 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1319 count = l->filelen / sizeof(*in);
1320 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1322 loadmodel->brushq1.texinfo = out;
1323 loadmodel->brushq1.numtexinfo = count;
1325 for (i = 0;i < count;i++, in++, out++)
1327 for (k = 0;k < 2;k++)
1328 for (j = 0;j < 4;j++)
1329 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1331 miptex = LittleLong(in->miptex);
1332 out->flags = LittleLong(in->flags);
1334 out->texture = NULL;
1335 if (loadmodel->brushq1.textures)
1337 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1338 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1340 out->texture = loadmodel->brushq1.textures + miptex;
1342 if (out->flags & TEX_SPECIAL)
1344 // if texture chosen is NULL or the shader needs a lightmap,
1345 // force to notexture water shader
1346 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1347 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1351 // if texture chosen is NULL, force to notexture
1352 if (out->texture == NULL)
1353 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1359 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1364 mins[0] = mins[1] = mins[2] = 9999;
1365 maxs[0] = maxs[1] = maxs[2] = -9999;
1367 for (i = 0;i < numverts;i++)
1369 for (j = 0;j < 3;j++, v++)
1379 #define MAX_SUBDIVPOLYTRIANGLES 4096
1380 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1382 static int subdivpolyverts, subdivpolytriangles;
1383 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1384 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1386 static int subdivpolylookupvert(vec3_t v)
1389 for (i = 0;i < subdivpolyverts;i++)
1390 if (subdivpolyvert[i][0] == v[0]
1391 && subdivpolyvert[i][1] == v[1]
1392 && subdivpolyvert[i][2] == v[2])
1394 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1395 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1396 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1397 return subdivpolyverts++;
1400 static void SubdividePolygon(int numverts, float *verts)
1402 int i, i1, i2, i3, f, b, c, p;
1403 vec3_t mins, maxs, front[256], back[256];
1404 float m, *pv, *cv, dist[256], frac;
1407 Host_Error("SubdividePolygon: ran out of verts in buffer");
1409 BoundPoly(numverts, verts, mins, maxs);
1411 for (i = 0;i < 3;i++)
1413 m = (mins[i] + maxs[i]) * 0.5;
1414 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1415 if (maxs[i] - m < 8)
1417 if (m - mins[i] < 8)
1421 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1422 dist[c] = cv[i] - m;
1425 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1429 VectorCopy(pv, front[f]);
1434 VectorCopy(pv, back[b]);
1437 if (dist[p] == 0 || dist[c] == 0)
1439 if ((dist[p] > 0) != (dist[c] > 0) )
1442 frac = dist[p] / (dist[p] - dist[c]);
1443 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1444 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1445 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1451 SubdividePolygon(f, front[0]);
1452 SubdividePolygon(b, back[0]);
1456 i1 = subdivpolylookupvert(verts);
1457 i2 = subdivpolylookupvert(verts + 3);
1458 for (i = 2;i < numverts;i++)
1460 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1462 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1466 i3 = subdivpolylookupvert(verts + i * 3);
1467 subdivpolyindex[subdivpolytriangles][0] = i1;
1468 subdivpolyindex[subdivpolytriangles][1] = i2;
1469 subdivpolyindex[subdivpolytriangles][2] = i3;
1471 subdivpolytriangles++;
1475 //Breaks a polygon up along axial 64 unit
1476 //boundaries so that turbulent and sky warps
1477 //can be done reasonably.
1478 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1484 subdivpolytriangles = 0;
1485 subdivpolyverts = 0;
1486 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1487 if (subdivpolytriangles < 1)
1488 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1490 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1491 mesh->num_vertices = subdivpolyverts;
1492 mesh->num_triangles = subdivpolytriangles;
1493 mesh->vertex = (surfvertex_t *)(mesh + 1);
1494 mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1495 memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1497 for (i = 0;i < mesh->num_triangles;i++)
1498 for (j = 0;j < 3;j++)
1499 mesh->index[i*3+j] = subdivpolyindex[i][j];
1501 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1503 VectorCopy(subdivpolyvert[i], v->v);
1504 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1505 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1510 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1513 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1514 mesh->num_vertices = numverts;
1515 mesh->num_triangles = numtriangles;
1516 mesh->data_vertex3f = (float *)(mesh + 1);
1517 mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1518 mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1519 mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1520 mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1521 mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1522 mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1523 mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1524 mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1525 mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1529 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1532 float *vec, *vert, mins[3], maxs[3], val, *v;
1535 // convert edges back to a normal polygon
1536 surf->poly_numverts = numedges;
1537 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1538 for (i = 0;i < numedges;i++)
1540 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1542 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1544 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1545 VectorCopy(vec, vert);
1549 // calculate polygon bounding box and center
1550 vert = surf->poly_verts;
1551 VectorCopy(vert, mins);
1552 VectorCopy(vert, maxs);
1554 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1556 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1557 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1558 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1560 VectorCopy(mins, surf->poly_mins);
1561 VectorCopy(maxs, surf->poly_maxs);
1562 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1563 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1564 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1566 // generate surface extents information
1567 tex = surf->texinfo;
1568 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1569 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1570 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1572 for (j = 0;j < 2;j++)
1574 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1581 for (i = 0;i < 2;i++)
1583 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1584 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1588 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1592 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1596 in = (void *)(mod_base + l->fileofs);
1597 if (l->filelen % sizeof(*in))
1598 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1599 count = l->filelen / sizeof(*in);
1600 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1602 loadmodel->brushq1.numsurfaces = count;
1603 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1604 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1605 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1607 for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, in++, surf++)
1609 surf->number = surfnum;
1610 // FIXME: validate edges, texinfo, etc?
1611 firstedge = LittleLong(in->firstedge);
1612 numedges = LittleShort(in->numedges);
1613 if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges)
1614 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1615 i = LittleShort(in->texinfo);
1616 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1617 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1618 surf->texinfo = loadmodel->brushq1.texinfo + i;
1619 surf->flags = surf->texinfo->texture->flags;
1621 planenum = LittleShort(in->planenum);
1622 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1623 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1625 if (LittleShort(in->side))
1626 surf->flags |= SURF_PLANEBACK;
1628 surf->plane = loadmodel->brushq1.planes + planenum;
1630 // clear lightmap (filled in later)
1631 surf->lightmaptexture = NULL;
1633 // force lightmap upload on first time seeing the surface
1634 surf->cached_dlight = true;
1636 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1638 ssize = (surf->extents[0] >> 4) + 1;
1639 tsize = (surf->extents[1] >> 4) + 1;
1642 for (i = 0;i < MAXLIGHTMAPS;i++)
1643 surf->styles[i] = in->styles[i];
1644 i = LittleLong(in->lightofs);
1646 surf->samples = NULL;
1647 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1648 surf->samples = loadmodel->brushq1.lightdata + i;
1649 else // LordHavoc: white lighting (bsp version 29)
1650 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1652 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1654 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1655 Host_Error("Bad surface extents");
1656 // stainmap for permanent marks on walls
1657 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1659 memset(surf->stainsamples, 255, ssize * tsize * 3);
1663 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1665 for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++)
1668 mesh->num_vertices = surf->poly_numverts;
1669 mesh->num_triangles = surf->poly_numverts - 2;
1670 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1671 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1672 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1673 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1674 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1675 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1676 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1677 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1678 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1679 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1681 surf->lightmaptexturestride = 0;
1682 surf->lightmaptexture = NULL;
1684 for (i = 0;i < mesh->num_vertices;i++)
1686 mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1687 mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1688 mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1689 s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1690 t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1691 mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1692 mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1693 mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1694 mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1695 mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1696 mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1697 mesh->data_lightmapoffsets[i] = 0;
1700 for (i = 0;i < mesh->num_triangles;i++)
1702 mesh->data_element3i[i * 3 + 0] = 0;
1703 mesh->data_element3i[i * 3 + 1] = i + 1;
1704 mesh->data_element3i[i * 3 + 2] = i + 2;
1707 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1708 Mod_BuildTextureVectorsAndNormals(mesh->num_vertices, mesh->num_triangles, mesh->data_vertex3f, mesh->data_texcoordtexture2f, mesh->data_element3i, mesh->data_svector3f, mesh->data_tvector3f, mesh->data_normal3f);
1710 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1712 int i, iu, iv, smax, tmax;
1713 float u, v, ubase, vbase, uscale, vscale;
1715 smax = surf->extents[0] >> 4;
1716 tmax = surf->extents[1] >> 4;
1718 surf->flags |= SURF_LIGHTMAP;
1719 if (r_miplightmaps.integer)
1721 surf->lightmaptexturestride = smax+1;
1722 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1726 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1727 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1729 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1730 uscale = (uscale - ubase) / (smax + 1);
1731 vscale = (vscale - vbase) / (tmax + 1);
1733 for (i = 0;i < mesh->num_vertices;i++)
1735 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1736 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1737 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1738 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1739 // LordHavoc: calc lightmap data offset for vertex lighting to use
1742 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1748 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1750 node->parent = parent;
1751 if (node->contents < 0)
1753 Mod_Q1BSP_SetParent(node->children[0], node);
1754 Mod_Q1BSP_SetParent(node->children[1], node);
1757 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1763 in = (void *)(mod_base + l->fileofs);
1764 if (l->filelen % sizeof(*in))
1765 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1766 count = l->filelen / sizeof(*in);
1767 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1769 loadmodel->brushq1.nodes = out;
1770 loadmodel->brushq1.numnodes = count;
1772 for ( i=0 ; i<count ; i++, in++, out++)
1774 for (j=0 ; j<3 ; j++)
1776 out->mins[j] = LittleShort(in->mins[j]);
1777 out->maxs[j] = LittleShort(in->maxs[j]);
1780 p = LittleLong(in->planenum);
1781 out->plane = loadmodel->brushq1.planes + p;
1783 out->firstsurface = LittleShort(in->firstface);
1784 out->numsurfaces = LittleShort(in->numfaces);
1786 for (j=0 ; j<2 ; j++)
1788 p = LittleShort(in->children[j]);
1790 out->children[j] = loadmodel->brushq1.nodes + p;
1792 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1796 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1799 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1803 int i, j, count, p, pvschainbytes;
1806 in = (void *)(mod_base + l->fileofs);
1807 if (l->filelen % sizeof(*in))
1808 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1809 count = l->filelen / sizeof(*in);
1810 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1812 loadmodel->brushq1.leafs = out;
1813 loadmodel->brushq1.numleafs = count;
1814 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1815 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1817 for ( i=0 ; i<count ; i++, in++, out++)
1819 for (j=0 ; j<3 ; j++)
1821 out->mins[j] = LittleShort(in->mins[j]);
1822 out->maxs[j] = LittleShort(in->maxs[j]);
1825 // FIXME: this function could really benefit from some error checking
1827 out->contents = LittleLong(in->contents);
1829 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1830 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1831 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
1833 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid marksurface range %i:%i outside range %i:%i\n", out->firstmarksurface, out->firstmarksurface + out->nummarksurfaces, 0, loadmodel->brushq1.nummarksurfaces);
1834 out->firstmarksurface = NULL;
1835 out->nummarksurfaces = 0;
1839 memset(out->pvsdata, 0xFF, pvschainbytes);
1840 pvs += pvschainbytes;
1842 p = LittleLong(in->visofs);
1845 if (p >= loadmodel->brushq1.num_compressedpvs)
1846 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
1848 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1851 for (j = 0;j < 4;j++)
1852 out->ambient_sound_level[j] = in->ambient_level[j];
1854 // FIXME: Insert caustics here
1858 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1860 dclipnode_t *in, *out;
1864 in = (void *)(mod_base + l->fileofs);
1865 if (l->filelen % sizeof(*in))
1866 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1867 count = l->filelen / sizeof(*in);
1868 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1870 loadmodel->brushq1.clipnodes = out;
1871 loadmodel->brushq1.numclipnodes = count;
1873 if (loadmodel->brush.ishlbsp)
1875 hull = &loadmodel->brushq1.hulls[1];
1876 hull->clipnodes = out;
1877 hull->firstclipnode = 0;
1878 hull->lastclipnode = count-1;
1879 hull->planes = loadmodel->brushq1.planes;
1880 hull->clip_mins[0] = -16;
1881 hull->clip_mins[1] = -16;
1882 hull->clip_mins[2] = -36;
1883 hull->clip_maxs[0] = 16;
1884 hull->clip_maxs[1] = 16;
1885 hull->clip_maxs[2] = 36;
1886 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1888 hull = &loadmodel->brushq1.hulls[2];
1889 hull->clipnodes = out;
1890 hull->firstclipnode = 0;
1891 hull->lastclipnode = count-1;
1892 hull->planes = loadmodel->brushq1.planes;
1893 hull->clip_mins[0] = -32;
1894 hull->clip_mins[1] = -32;
1895 hull->clip_mins[2] = -32;
1896 hull->clip_maxs[0] = 32;
1897 hull->clip_maxs[1] = 32;
1898 hull->clip_maxs[2] = 32;
1899 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1901 hull = &loadmodel->brushq1.hulls[3];
1902 hull->clipnodes = out;
1903 hull->firstclipnode = 0;
1904 hull->lastclipnode = count-1;
1905 hull->planes = loadmodel->brushq1.planes;
1906 hull->clip_mins[0] = -16;
1907 hull->clip_mins[1] = -16;
1908 hull->clip_mins[2] = -18;
1909 hull->clip_maxs[0] = 16;
1910 hull->clip_maxs[1] = 16;
1911 hull->clip_maxs[2] = 18;
1912 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1916 hull = &loadmodel->brushq1.hulls[1];
1917 hull->clipnodes = out;
1918 hull->firstclipnode = 0;
1919 hull->lastclipnode = count-1;
1920 hull->planes = loadmodel->brushq1.planes;
1921 hull->clip_mins[0] = -16;
1922 hull->clip_mins[1] = -16;
1923 hull->clip_mins[2] = -24;
1924 hull->clip_maxs[0] = 16;
1925 hull->clip_maxs[1] = 16;
1926 hull->clip_maxs[2] = 32;
1927 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1929 hull = &loadmodel->brushq1.hulls[2];
1930 hull->clipnodes = out;
1931 hull->firstclipnode = 0;
1932 hull->lastclipnode = count-1;
1933 hull->planes = loadmodel->brushq1.planes;
1934 hull->clip_mins[0] = -32;
1935 hull->clip_mins[1] = -32;
1936 hull->clip_mins[2] = -24;
1937 hull->clip_maxs[0] = 32;
1938 hull->clip_maxs[1] = 32;
1939 hull->clip_maxs[2] = 64;
1940 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1943 for (i=0 ; i<count ; i++, out++, in++)
1945 out->planenum = LittleLong(in->planenum);
1946 out->children[0] = LittleShort(in->children[0]);
1947 out->children[1] = LittleShort(in->children[1]);
1948 if (out->children[0] >= count || out->children[1] >= count)
1949 Host_Error("Corrupt clipping hull(out of range child)\n");
1953 //Duplicate the drawing hull structure as a clipping hull
1954 static void Mod_Q1BSP_MakeHull0(void)
1961 hull = &loadmodel->brushq1.hulls[0];
1963 in = loadmodel->brushq1.nodes;
1964 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1966 hull->clipnodes = out;
1967 hull->firstclipnode = 0;
1968 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1969 hull->planes = loadmodel->brushq1.planes;
1971 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1973 out->planenum = in->plane - loadmodel->brushq1.planes;
1974 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1975 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1979 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1984 in = (void *)(mod_base + l->fileofs);
1985 if (l->filelen % sizeof(*in))
1986 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1987 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1988 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1990 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1992 j = (unsigned) LittleShort(in[i]);
1993 if (j >= loadmodel->brushq1.numsurfaces)
1994 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
1995 loadmodel->brushq1.marksurfaces[i] = j;
1999 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2004 in = (void *)(mod_base + l->fileofs);
2005 if (l->filelen % sizeof(*in))
2006 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2007 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2008 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2010 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2011 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2015 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2021 in = (void *)(mod_base + l->fileofs);
2022 if (l->filelen % sizeof(*in))
2023 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2025 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2026 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2028 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2030 out->normal[0] = LittleFloat(in->normal[0]);
2031 out->normal[1] = LittleFloat(in->normal[1]);
2032 out->normal[2] = LittleFloat(in->normal[2]);
2033 out->dist = LittleFloat(in->dist);
2039 typedef struct portal_s
2042 mnode_t *nodes[2]; // [0] = front side of plane
2043 struct portal_s *next[2];
2045 struct portal_s *chain; // all portals are linked into a list
2049 static portal_t *portalchain;
2056 static portal_t *AllocPortal(void)
2059 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2060 p->chain = portalchain;
2065 static void FreePortal(portal_t *p)
2070 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2072 // calculate children first
2073 if (node->children[0]->contents >= 0)
2074 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2075 if (node->children[1]->contents >= 0)
2076 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2078 // make combined bounding box from children
2079 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2080 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2081 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2082 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2083 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2084 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2087 static void Mod_Q1BSP_FinalizePortals(void)
2089 int i, j, numportals, numpoints;
2090 portal_t *p, *pnext;
2093 mleaf_t *leaf, *endleaf;
2096 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2097 leaf = loadmodel->brushq1.leafs;
2098 endleaf = leaf + loadmodel->brushq1.numleafs;
2099 for (;leaf < endleaf;leaf++)
2101 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2102 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2109 for (i = 0;i < 2;i++)
2111 leaf = (mleaf_t *)p->nodes[i];
2113 for (j = 0;j < w->numpoints;j++)
2115 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2116 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2117 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2118 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2119 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2120 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2127 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2129 // tally up portal and point counts
2135 // note: this check must match the one below or it will usually corrupt memory
2136 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2137 if (p->winding && p->nodes[0] != p->nodes[1]
2138 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2139 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2142 numpoints += p->winding->numpoints * 2;
2146 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2147 loadmodel->brushq1.numportals = numportals;
2148 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2149 loadmodel->brushq1.numportalpoints = numpoints;
2150 // clear all leaf portal chains
2151 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2152 loadmodel->brushq1.leafs[i].portals = NULL;
2153 // process all portals in the global portal chain, while freeing them
2154 portal = loadmodel->brushq1.portals;
2155 point = loadmodel->brushq1.portalpoints;
2164 // note: this check must match the one above or it will usually corrupt memory
2165 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2166 if (p->nodes[0] != p->nodes[1]
2167 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2168 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2170 // first make the back to front portal(forward portal)
2171 portal->points = point;
2172 portal->numpoints = p->winding->numpoints;
2173 portal->plane.dist = p->plane.dist;
2174 VectorCopy(p->plane.normal, portal->plane.normal);
2175 portal->here = (mleaf_t *)p->nodes[1];
2176 portal->past = (mleaf_t *)p->nodes[0];
2178 for (j = 0;j < portal->numpoints;j++)
2180 VectorCopy(p->winding->points[j], point->position);
2183 PlaneClassify(&portal->plane);
2185 // link into leaf's portal chain
2186 portal->next = portal->here->portals;
2187 portal->here->portals = portal;
2189 // advance to next portal
2192 // then make the front to back portal(backward portal)
2193 portal->points = point;
2194 portal->numpoints = p->winding->numpoints;
2195 portal->plane.dist = -p->plane.dist;
2196 VectorNegate(p->plane.normal, portal->plane.normal);
2197 portal->here = (mleaf_t *)p->nodes[0];
2198 portal->past = (mleaf_t *)p->nodes[1];
2200 for (j = portal->numpoints - 1;j >= 0;j--)
2202 VectorCopy(p->winding->points[j], point->position);
2205 PlaneClassify(&portal->plane);
2207 // link into leaf's portal chain
2208 portal->next = portal->here->portals;
2209 portal->here->portals = portal;
2211 // advance to next portal
2214 Winding_Free(p->winding);
2226 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2229 Host_Error("AddPortalToNodes: NULL front node");
2231 Host_Error("AddPortalToNodes: NULL back node");
2232 if (p->nodes[0] || p->nodes[1])
2233 Host_Error("AddPortalToNodes: already included");
2234 // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
2236 p->nodes[0] = front;
2237 p->next[0] = (portal_t *)front->portals;
2238 front->portals = (mportal_t *)p;
2241 p->next[1] = (portal_t *)back->portals;
2242 back->portals = (mportal_t *)p;
2247 RemovePortalFromNode
2250 static void RemovePortalFromNodes(portal_t *portal)
2254 void **portalpointer;
2256 for (i = 0;i < 2;i++)
2258 node = portal->nodes[i];
2260 portalpointer = (void **) &node->portals;
2265 Host_Error("RemovePortalFromNodes: portal not in leaf");
2269 if (portal->nodes[0] == node)
2271 *portalpointer = portal->next[0];
2272 portal->nodes[0] = NULL;
2274 else if (portal->nodes[1] == node)
2276 *portalpointer = portal->next[1];
2277 portal->nodes[1] = NULL;
2280 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2284 if (t->nodes[0] == node)
2285 portalpointer = (void **) &t->next[0];
2286 else if (t->nodes[1] == node)
2287 portalpointer = (void **) &t->next[1];
2289 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2294 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2297 mnode_t *front, *back, *other_node;
2298 mplane_t clipplane, *plane;
2299 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2300 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2302 // if a leaf, we're done
2306 plane = node->plane;
2308 front = node->children[0];
2309 back = node->children[1];
2311 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2313 // create the new portal by generating a polygon for the node plane,
2314 // and clipping it by all of the other portals(which came from nodes above this one)
2315 nodeportal = AllocPortal();
2316 nodeportal->plane = *plane;
2318 nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2319 side = 0; // shut up compiler warning
2320 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2322 clipplane = portal->plane;
2323 if (portal->nodes[0] == portal->nodes[1])
2324 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2325 if (portal->nodes[0] == node)
2327 else if (portal->nodes[1] == node)
2329 clipplane.dist = -clipplane.dist;
2330 VectorNegate(clipplane.normal, clipplane.normal);
2334 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2336 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2337 if (!nodeportalwinding)
2339 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2344 if (nodeportalwinding)
2346 // if the plane was not clipped on all sides, there was an error
2347 nodeportal->winding = nodeportalwinding;
2348 AddPortalToNodes(nodeportal, front, back);
2351 // split the portals of this node along this node's plane and assign them to the children of this node
2352 // (migrating the portals downward through the tree)
2353 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2355 if (portal->nodes[0] == portal->nodes[1])
2356 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2357 if (portal->nodes[0] == node)
2359 else if (portal->nodes[1] == node)
2362 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2363 nextportal = portal->next[side];
2365 other_node = portal->nodes[!side];
2366 RemovePortalFromNodes(portal);
2368 // cut the portal into two portals, one on each side of the node plane
2369 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2374 AddPortalToNodes(portal, back, other_node);
2376 AddPortalToNodes(portal, other_node, back);
2382 AddPortalToNodes(portal, front, other_node);
2384 AddPortalToNodes(portal, other_node, front);
2388 // the winding is split
2389 splitportal = AllocPortal();
2390 temp = splitportal->chain;
2391 *splitportal = *portal;
2392 splitportal->chain = temp;
2393 splitportal->winding = backwinding;
2394 Winding_Free(portal->winding);
2395 portal->winding = frontwinding;
2399 AddPortalToNodes(portal, front, other_node);
2400 AddPortalToNodes(splitportal, back, other_node);
2404 AddPortalToNodes(portal, other_node, front);
2405 AddPortalToNodes(splitportal, other_node, back);
2409 Mod_Q1BSP_RecursiveNodePortals(front);
2410 Mod_Q1BSP_RecursiveNodePortals(back);
2413 static void Mod_Q1BSP_MakePortals(void)
2416 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2417 Mod_Q1BSP_FinalizePortals();
2420 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2423 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2424 msurface_t *surf, *s;
2425 float *v0, *v1, *v2, *v3;
2426 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2427 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2428 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2430 for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
2432 if (surf->neighborsurfaces[vertnum])
2434 surf->neighborsurfaces[vertnum] = NULL;
2435 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2437 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2438 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2439 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2442 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2443 if (s->neighborsurfaces[vnum] == surf)
2445 if (vnum < s->poly_numverts)
2447 for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
2449 if (s->neighborsurfaces[vnum] == NULL
2450 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2451 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2453 surf->neighborsurfaces[vertnum] = s;
2454 s->neighborsurfaces[vnum] = surf;
2458 if (vnum < s->poly_numverts)
2466 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2468 int i, j, stylecounts[256], totalcount, remapstyles[256];
2470 memset(stylecounts, 0, sizeof(stylecounts));
2471 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2473 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2474 for (j = 0;j < MAXLIGHTMAPS;j++)
2475 stylecounts[surf->styles[j]]++;
2478 model->brushq1.light_styles = 0;
2479 for (i = 0;i < 255;i++)
2483 remapstyles[i] = model->brushq1.light_styles++;
2484 totalcount += stylecounts[i] + 1;
2489 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2490 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2491 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2492 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2493 model->brushq1.light_styles = 0;
2494 for (i = 0;i < 255;i++)
2496 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2498 for (i = 0;i < model->brushq1.light_styles;i++)
2500 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2501 j += stylecounts[model->brushq1.light_style[i]] + 1;
2503 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2505 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2506 for (j = 0;j < MAXLIGHTMAPS;j++)
2507 if (surf->styles[j] != 255)
2508 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2511 for (i = 0;i < model->brushq1.light_styles;i++)
2513 *model->brushq1.light_styleupdatechains[i] = NULL;
2514 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2515 j += stylecounts[model->brushq1.light_style[i]] + 1;
2519 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2522 for (i = 0;i < model->brushq1.numtextures;i++)
2523 model->brushq1.pvstexturechainslength[i] = 0;
2524 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2526 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2528 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2529 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2532 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2534 if (model->brushq1.pvstexturechainslength[i])
2536 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2537 j += model->brushq1.pvstexturechainslength[i] + 1;
2540 model->brushq1.pvstexturechains[i] = NULL;
2542 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2543 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2544 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2545 for (i = 0;i < model->brushq1.numtextures;i++)
2547 if (model->brushq1.pvstexturechainslength[i])
2549 *model->brushq1.pvstexturechains[i] = NULL;
2550 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2555 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2560 while (node->contents >= 0)
2562 d = PlaneDiff(org, node->plane);
2564 node = node->children[0];
2565 else if (d < -radius)
2566 node = node->children[1];
2569 // go down both sides
2570 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2571 node = node->children[1];
2575 // if this is a leaf, accumulate the pvs bits
2576 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2577 for (i = 0;i < pvsbytes;i++)
2578 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2581 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2582 //of the given point.
2583 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2585 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2586 bytes = min(bytes, pvsbufferlength);
2587 memset(pvsbuffer, 0, bytes);
2588 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2592 //Returns PVS data for a given point
2593 //(note: always returns valid data, never NULL)
2594 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2597 Mod_CheckLoaded(model);
2598 // LordHavoc: modified to start at first clip node,
2599 // in other words: first node of the (sub)model
2600 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2601 while (node->contents == 0)
2602 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2603 return ((mleaf_t *)node)->pvsdata;
2606 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2611 VectorSubtract(inmaxs, inmins, size);
2612 if (cmodel->brush.ishlbsp)
2615 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2616 else if (size[0] <= 32)
2618 if (size[2] < 54) // pick the nearest of 36 or 72
2619 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2621 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2624 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2629 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2630 else if (size[0] <= 32)
2631 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2633 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2635 VectorCopy(inmins, outmins);
2636 VectorAdd(inmins, hull->clip_size, outmaxs);
2639 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2640 extern void R_Model_Brush_Draw(entity_render_t *ent);
2641 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2642 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
2643 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2648 mempool_t *mainmempool;
2650 model_t *originalloadmodel;
2651 float dist, modelyawradius, modelradius, *vec;
2654 mod->type = mod_brush;
2656 header = (dheader_t *)buffer;
2658 i = LittleLong(header->version);
2659 if (i != BSPVERSION && i != 30)
2660 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2661 mod->brush.ishlbsp = i == 30;
2663 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2664 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2665 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2666 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2667 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2668 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2669 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2670 mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
2671 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2672 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2673 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2674 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2676 if (loadmodel->isworldmodel)
2678 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2679 // until we get a texture for it...
2683 // swap all the lumps
2684 mod_base = (qbyte *)header;
2686 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2687 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2691 // store which lightmap format to use
2692 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2694 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2695 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2696 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2697 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2698 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2699 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2700 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2701 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2702 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2703 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2704 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2705 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2706 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2707 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2708 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2710 if (mod->brushq1.data_compressedpvs)
2711 Mem_Free(mod->brushq1.data_compressedpvs);
2712 mod->brushq1.data_compressedpvs = NULL;
2713 mod->brushq1.num_compressedpvs = 0;
2715 Mod_Q1BSP_MakeHull0();
2716 Mod_Q1BSP_MakePortals();
2718 if (developer.integer)
2719 Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.numleafs - 1, loadmodel->brushq1.numportals);
2721 mod->numframes = 2; // regular and alternate animation
2723 mainmempool = mod->mempool;
2724 loadname = mod->name;
2726 Mod_Q1BSP_LoadLightList();
2727 originalloadmodel = loadmodel;
2730 // set up the submodels(FIXME: this is confusing)
2732 for (i = 0;i < mod->brush.numsubmodels;i++)
2734 bm = &mod->brushq1.submodels[i];
2736 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2737 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2739 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2740 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2743 mod->brushq1.firstmodelsurface = bm->firstface;
2744 mod->brushq1.nummodelsurfaces = bm->numfaces;
2746 // this gets altered below if sky is used
2747 mod->DrawSky = NULL;
2748 mod->Draw = R_Model_Brush_Draw;
2749 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2750 mod->DrawLight = R_Model_Brush_DrawLight;
2751 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2752 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2753 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2754 Mod_Q1BSP_BuildPVSTextureChains(mod);
2755 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2756 if (mod->brushq1.nummodelsurfaces)
2758 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2759 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2760 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2763 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2765 // we only need to have a drawsky function if it is used(usually only on world model)
2766 if (surf->texinfo->texture->shader == &Cshader_sky)
2767 mod->DrawSky = R_Model_Brush_DrawSky;
2768 // LordHavoc: submodels always clip, even if water
2769 if (mod->brush.numsubmodels - 1)
2770 surf->flags |= SURF_SOLIDCLIP;
2771 // calculate bounding shapes
2772 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
2774 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2775 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2776 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2777 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2778 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2779 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2780 dist = vec[0]*vec[0]+vec[1]*vec[1];
2781 if (modelyawradius < dist)
2782 modelyawradius = dist;
2783 dist += vec[2]*vec[2];
2784 if (modelradius < dist)
2788 modelyawradius = sqrt(modelyawradius);
2789 modelradius = sqrt(modelradius);
2790 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2791 mod->yawmins[2] = mod->normalmins[2];
2792 mod->yawmaxs[2] = mod->normalmaxs[2];
2793 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2794 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2795 mod->radius = modelradius;
2796 mod->radius2 = modelradius * modelradius;
2800 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2801 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2803 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2805 mod->brushq1.visleafs = bm->visleafs;
2807 // LordHavoc: only register submodels if it is the world
2808 // (prevents bsp models from replacing world submodels)
2809 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2812 // duplicate the basic information
2813 sprintf(name, "*%i", i+1);
2814 loadmodel = Mod_FindName(name);
2816 strcpy(loadmodel->name, name);
2817 // textures and memory belong to the main model
2818 loadmodel->texturepool = NULL;
2819 loadmodel->mempool = NULL;
2824 loadmodel = originalloadmodel;
2825 //Mod_Q1BSP_ProcessLightList();
2828 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2832 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2839 in = (void *)(mod_base + l->fileofs);
2840 if (l->filelen % sizeof(*in))
2841 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2842 count = l->filelen / sizeof(*in);
2843 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2846 loadmodel->num = count;
2848 for (i = 0;i < count;i++, in++, out++)
2854 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2861 in = (void *)(mod_base + l->fileofs);
2862 if (l->filelen % sizeof(*in))
2863 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2864 count = l->filelen / sizeof(*in);
2865 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2868 loadmodel->num = count;
2870 for (i = 0;i < count;i++, in++, out++)
2876 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2883 in = (void *)(mod_base + l->fileofs);
2884 if (l->filelen % sizeof(*in))
2885 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2886 count = l->filelen / sizeof(*in);
2887 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2890 loadmodel->num = count;
2892 for (i = 0;i < count;i++, in++, out++)
2898 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2905 in = (void *)(mod_base + l->fileofs);
2906 if (l->filelen % sizeof(*in))
2907 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2908 count = l->filelen / sizeof(*in);
2909 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2912 loadmodel->num = count;
2914 for (i = 0;i < count;i++, in++, out++)
2920 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2927 in = (void *)(mod_base + l->fileofs);
2928 if (l->filelen % sizeof(*in))
2929 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2930 count = l->filelen / sizeof(*in);
2931 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2934 loadmodel->num = count;
2936 for (i = 0;i < count;i++, in++, out++)
2942 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2949 in = (void *)(mod_base + l->fileofs);
2950 if (l->filelen % sizeof(*in))
2951 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2952 count = l->filelen / sizeof(*in);
2953 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2956 loadmodel->num = count;
2958 for (i = 0;i < count;i++, in++, out++)
2964 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2971 in = (void *)(mod_base + l->fileofs);
2972 if (l->filelen % sizeof(*in))
2973 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2974 count = l->filelen / sizeof(*in);
2975 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2978 loadmodel->num = count;
2980 for (i = 0;i < count;i++, in++, out++)
2986 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
2993 in = (void *)(mod_base + l->fileofs);
2994 if (l->filelen % sizeof(*in))
2995 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2996 count = l->filelen / sizeof(*in);
2997 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3000 loadmodel->num = count;
3002 for (i = 0;i < count;i++, in++, out++)
3008 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3015 in = (void *)(mod_base + l->fileofs);
3016 if (l->filelen % sizeof(*in))
3017 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3018 count = l->filelen / sizeof(*in);
3019 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3022 loadmodel->num = count;
3024 for (i = 0;i < count;i++, in++, out++)
3030 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3037 in = (void *)(mod_base + l->fileofs);
3038 if (l->filelen % sizeof(*in))
3039 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3040 count = l->filelen / sizeof(*in);
3041 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3044 loadmodel->num = count;
3046 for (i = 0;i < count;i++, in++, out++)
3052 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3059 in = (void *)(mod_base + l->fileofs);
3060 if (l->filelen % sizeof(*in))
3061 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3062 count = l->filelen / sizeof(*in);
3063 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3066 loadmodel->num = count;
3068 for (i = 0;i < count;i++, in++, out++)
3074 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3081 in = (void *)(mod_base + l->fileofs);
3082 if (l->filelen % sizeof(*in))
3083 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3084 count = l->filelen / sizeof(*in);
3085 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3088 loadmodel->num = count;
3090 for (i = 0;i < count;i++, in++, out++)
3096 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3103 in = (void *)(mod_base + l->fileofs);
3104 if (l->filelen % sizeof(*in))
3105 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3106 count = l->filelen / sizeof(*in);
3107 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3110 loadmodel->num = count;
3112 for (i = 0;i < count;i++, in++, out++)
3118 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3125 in = (void *)(mod_base + l->fileofs);
3126 if (l->filelen % sizeof(*in))
3127 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3128 count = l->filelen / sizeof(*in);
3129 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3132 loadmodel->num = count;
3134 for (i = 0;i < count;i++, in++, out++)
3140 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3147 in = (void *)(mod_base + l->fileofs);
3148 if (l->filelen % sizeof(*in))
3149 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3150 count = l->filelen / sizeof(*in);
3151 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3154 loadmodel->num = count;
3156 for (i = 0;i < count;i++, in++, out++)
3162 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3169 in = (void *)(mod_base + l->fileofs);
3170 if (l->filelen % sizeof(*in))
3171 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3172 count = l->filelen / sizeof(*in);
3173 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3176 loadmodel->num = count;
3178 for (i = 0;i < count;i++, in++, out++)
3184 static void Mod_Q2BSP_LoadModels(lump_t *l)
3191 in = (void *)(mod_base + l->fileofs);
3192 if (l->filelen % sizeof(*in))
3193 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3194 count = l->filelen / sizeof(*in);
3195 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3198 loadmodel->num = count;
3200 for (i = 0;i < count;i++, in++, out++)
3206 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3209 q2dheader_t *header;
3211 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3213 mod->type = mod_brushq2;
3215 header = (q2dheader_t *)buffer;
3217 i = LittleLong(header->version);
3218 if (i != Q2BSPVERSION)
3219 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3220 mod->brush.ishlbsp = false;
3221 if (loadmodel->isworldmodel)
3223 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3224 // until we get a texture for it...
3228 mod_base = (qbyte *)header;
3230 // swap all the lumps
3231 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3232 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3234 // store which lightmap format to use
3235 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3237 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3238 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3239 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3240 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3241 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3242 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3243 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3244 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3245 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3246 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3247 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3248 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3249 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3250 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3251 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3252 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3253 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3254 // LordHavoc: must go last because this makes the submodels
3255 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3258 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3259 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3261 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3264 char key[128], value[4096];
3266 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3267 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3268 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3271 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3272 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3273 data = loadmodel->brush.entities;
3274 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3275 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3279 if (!COM_ParseToken(&data, false))
3281 if (com_token[0] == '}')
3282 break; // end of worldspawn
3283 if (com_token[0] == '_')
3284 strcpy(key, com_token + 1);
3286 strcpy(key, com_token);
3287 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3288 key[strlen(key)-1] = 0;
3289 if (!COM_ParseToken(&data, false))
3291 strcpy(value, com_token);
3292 if (!strcmp("gridsize", key))
3294 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3295 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3301 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3307 in = (void *)(mod_base + l->fileofs);
3308 if (l->filelen % sizeof(*in))
3309 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3310 count = l->filelen / sizeof(*in);
3311 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3313 loadmodel->brushq3.data_textures = out;
3314 loadmodel->brushq3.num_textures = count;
3316 for (i = 0;i < count;i++, in++, out++)
3318 strlcpy (out->name, in->name, sizeof (out->name));
3319 out->surfaceflags = LittleLong(in->surfaceflags);
3320 out->nativecontents = LittleLong(in->contents);
3321 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3322 out->renderflags = 0;
3323 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3324 out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3325 if (!strncmp(out->name, "textures/skies/", 15))
3326 out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY;
3329 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3333 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3339 in = (void *)(mod_base + l->fileofs);
3340 if (l->filelen % sizeof(*in))
3341 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3342 count = l->filelen / sizeof(*in);
3343 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3345 loadmodel->brushq3.data_planes = out;
3346 loadmodel->brushq3.num_planes = count;
3348 for (i = 0;i < count;i++, in++, out++)
3350 out->normal[0] = LittleLong(in->normal[0]);
3351 out->normal[1] = LittleLong(in->normal[1]);
3352 out->normal[2] = LittleLong(in->normal[2]);
3353 out->dist = LittleLong(in->dist);
3358 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3361 q3mbrushside_t *out;
3364 in = (void *)(mod_base + l->fileofs);
3365 if (l->filelen % sizeof(*in))
3366 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3367 count = l->filelen / sizeof(*in);
3368 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3370 loadmodel->brushq3.data_brushsides = out;
3371 loadmodel->brushq3.num_brushsides = count;
3373 for (i = 0;i < count;i++, in++, out++)
3375 n = LittleLong(in->planeindex);
3376 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3377 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3378 out->plane = loadmodel->brushq3.data_planes + n;
3379 n = LittleLong(in->textureindex);
3380 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3381 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3382 out->texture = loadmodel->brushq3.data_textures + n;
3386 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3390 int i, j, n, c, count, maxplanes;
3392 winding_t *temp1, *temp2;
3394 in = (void *)(mod_base + l->fileofs);
3395 if (l->filelen % sizeof(*in))
3396 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3397 count = l->filelen / sizeof(*in);
3398 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3400 loadmodel->brushq3.data_brushes = out;
3401 loadmodel->brushq3.num_brushes = count;
3403 temp1 = Winding_New(64);
3404 temp2 = Winding_New(64);
3409 for (i = 0;i < count;i++, in++, out++)
3411 n = LittleLong(in->firstbrushside);
3412 c = LittleLong(in->numbrushsides);
3413 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3414 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3415 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3416 out->numbrushsides = c;
3417 n = LittleLong(in->textureindex);
3418 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3419 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3420 out->texture = loadmodel->brushq3.data_textures + n;
3422 // make a list of mplane_t structs to construct a colbrush from
3423 if (maxplanes < out->numbrushsides)
3425 maxplanes = out->numbrushsides;
3428 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3430 for (j = 0;j < out->numbrushsides;j++)
3432 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3433 planes[j].dist = out->firstbrushside[j].plane->dist;
3435 // make the colbrush from the planes
3436 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
3440 Winding_Free(temp1);
3441 Winding_Free(temp2);
3444 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3450 in = (void *)(mod_base + l->fileofs);
3451 if (l->filelen % sizeof(*in))
3452 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3453 count = l->filelen / sizeof(*in);
3454 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3456 loadmodel->brushq3.data_effects = out;
3457 loadmodel->brushq3.num_effects = count;
3459 for (i = 0;i < count;i++, in++, out++)
3461 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3462 n = LittleLong(in->brushindex);
3463 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3464 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3465 out->brush = loadmodel->brushq3.data_brushes + n;
3466 out->unknown = LittleLong(in->unknown);
3470 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3475 in = (void *)(mod_base + l->fileofs);
3476 if (l->filelen % sizeof(*in))
3477 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3478 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3479 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3480 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3481 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3482 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3483 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3484 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3485 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3487 for (i = 0;i < count;i++, in++)
3489 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3490 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3491 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3492 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3493 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3494 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3495 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3496 // svector/tvector are calculated later in face loading
3497 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3498 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3499 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3500 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3501 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3502 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3503 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3504 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3505 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3506 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3507 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3508 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3509 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3513 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3519 in = (void *)(mod_base + l->fileofs);
3520 if (l->filelen % sizeof(int[3]))
3521 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3522 count = l->filelen / sizeof(*in);
3523 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3525 loadmodel->brushq3.num_triangles = count / 3;
3526 loadmodel->brushq3.data_element3i = out;
3527 loadmodel->brushq3.data_neighbor3i = out + count;
3529 for (i = 0;i < count;i++, in++, out++)
3531 *out = LittleLong(*in);
3532 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3534 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3540 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3548 in = (void *)(mod_base + l->fileofs);
3549 if (l->filelen % sizeof(*in))
3550 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3551 count = l->filelen / sizeof(*in);
3552 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3554 loadmodel->brushq3.data_lightmaps = out;
3555 loadmodel->brushq3.num_lightmaps = count;
3557 for (i = 0;i < count;i++, in++, out++)
3558 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3561 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3565 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3566 //int *originalelement3i;
3567 //int *originalneighbor3i;
3568 float *originalvertex3f;
3569 //float *originalsvector3f;
3570 //float *originaltvector3f;
3571 //float *originalnormal3f;
3572 float *originalcolor4f;
3573 float *originaltexcoordtexture2f;
3574 float *originaltexcoordlightmap2f;
3577 in = (void *)(mod_base + l->fileofs);
3578 if (l->filelen % sizeof(*in))
3579 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3580 count = l->filelen / sizeof(*in);
3581 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3583 loadmodel->brushq3.data_faces = out;
3584 loadmodel->brushq3.num_faces = count;
3586 for (i = 0;i < count;i++, in++, out++)
3588 // check face type first
3589 out->type = LittleLong(in->type);
3590 if (out->type != Q3FACETYPE_POLYGON
3591 && out->type != Q3FACETYPE_PATCH
3592 && out->type != Q3FACETYPE_MESH
3593 && out->type != Q3FACETYPE_FLARE)
3595 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3596 out->type = 0; // error
3600 n = LittleLong(in->textureindex);
3601 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3603 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3604 out->type = 0; // error
3608 out->texture = loadmodel->brushq3.data_textures + n;
3609 n = LittleLong(in->effectindex);
3610 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3612 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3618 out->effect = loadmodel->brushq3.data_effects + n;
3619 n = LittleLong(in->lightmapindex);
3620 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3622 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3626 out->lightmaptexture = NULL;
3628 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3630 out->firstvertex = LittleLong(in->firstvertex);
3631 out->num_vertices = LittleLong(in->numvertices);
3632 out->firstelement = LittleLong(in->firstelement);
3633 out->num_triangles = LittleLong(in->numelements) / 3;
3634 if (out->num_triangles * 3 != LittleLong(in->numelements))
3636 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));
3637 out->type = 0; // error
3640 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3642 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);
3643 out->type = 0; // error
3646 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3648 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);
3649 out->type = 0; // error
3652 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3653 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3654 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3655 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3656 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3657 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3658 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3659 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3660 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3663 case Q3FACETYPE_POLYGON:
3664 case Q3FACETYPE_MESH:
3665 // no processing necessary
3667 case Q3FACETYPE_PATCH:
3668 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3669 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3670 if (patchsize[0] < 1 || patchsize[1] < 1)
3672 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3673 out->type = 0; // error
3676 // convert patch to Q3FACETYPE_MESH
3677 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3678 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3679 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3680 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3681 finalvertices = finalwidth * finalheight;
3682 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3683 originalvertex3f = out->data_vertex3f;
3684 //originalsvector3f = out->data_svector3f;
3685 //originaltvector3f = out->data_tvector3f;
3686 //originalnormal3f = out->data_normal3f;
3687 originalcolor4f = out->data_color4f;
3688 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3689 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3690 //originalelement3i = out->data_element3i;
3691 //originalneighbor3i = out->data_neighbor3i;
3692 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
3693 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3694 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3695 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3696 out->data_color4f = out->data_normal3f + finalvertices * 3;
3697 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3698 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3699 out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
3700 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3701 out->type = Q3FACETYPE_MESH;
3702 out->firstvertex = -1;
3703 out->num_vertices = finalvertices;
3704 out->firstelement = -1;
3705 out->num_triangles = finaltriangles;
3706 // generate geometry
3707 // (note: normals are skipped because they get recalculated)
3708 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3709 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3710 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3711 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3712 // generate elements
3713 e = out->data_element3i;
3714 for (y = 0;y < finalheight - 1;y++)
3716 row0 = (y + 0) * finalwidth;
3717 row1 = (y + 1) * finalwidth;
3718 for (x = 0;x < finalwidth - 1;x++)
3730 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3731 if (developer.integer)
3733 if (out->num_triangles < finaltriangles)
3734 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);
3736 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);
3738 // q3map does not put in collision brushes for curves... ugh
3739 out->collisions = true;
3741 case Q3FACETYPE_FLARE:
3742 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3744 out->num_triangles = 0;
3747 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
3748 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3750 if (invalidelements)
3752 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);
3753 for (j = 0;j < out->num_triangles * 3;j++)
3755 Con_Printf(" %i", out->data_element3i[j]);
3756 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3757 out->data_element3i[j] = 0;
3761 // for shadow volumes
3762 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
3763 // for per pixel lighting
3764 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);
3765 // calculate a bounding box
3766 VectorClear(out->mins);
3767 VectorClear(out->maxs);
3768 if (out->num_vertices)
3770 VectorCopy(out->data_vertex3f, out->mins);
3771 VectorCopy(out->data_vertex3f, out->maxs);
3772 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
3774 out->mins[0] = min(out->mins[0], v[0]);
3775 out->maxs[0] = max(out->maxs[0], v[0]);
3776 out->mins[1] = min(out->mins[1], v[1]);
3777 out->maxs[1] = max(out->maxs[1], v[1]);
3778 out->mins[2] = min(out->mins[2], v[2]);
3779 out->maxs[2] = max(out->maxs[2], v[2]);
3781 out->mins[0] -= 1.0f;
3782 out->mins[1] -= 1.0f;
3783 out->mins[2] -= 1.0f;
3784 out->maxs[0] += 1.0f;
3785 out->maxs[1] += 1.0f;
3786 out->maxs[2] += 1.0f;
3791 static void Mod_Q3BSP_LoadModels(lump_t *l)
3795 int i, j, n, c, count;
3797 in = (void *)(mod_base + l->fileofs);
3798 if (l->filelen % sizeof(*in))
3799 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3800 count = l->filelen / sizeof(*in);
3801 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3803 loadmodel->brushq3.data_models = out;
3804 loadmodel->brushq3.num_models = count;
3806 for (i = 0;i < count;i++, in++, out++)
3808 for (j = 0;j < 3;j++)
3810 out->mins[j] = LittleFloat(in->mins[j]);
3811 out->maxs[j] = LittleFloat(in->maxs[j]);
3813 n = LittleLong(in->firstface);
3814 c = LittleLong(in->numfaces);
3815 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3816 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3817 out->firstface = loadmodel->brushq3.data_faces + n;
3819 n = LittleLong(in->firstbrush);
3820 c = LittleLong(in->numbrushes);
3821 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3822 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3823 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3824 out->numbrushes = c;
3828 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3834 in = (void *)(mod_base + l->fileofs);
3835 if (l->filelen % sizeof(*in))
3836 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3837 count = l->filelen / sizeof(*in);
3838 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3840 loadmodel->brushq3.data_leafbrushes = out;
3841 loadmodel->brushq3.num_leafbrushes = count;
3843 for (i = 0;i < count;i++, in++, out++)
3845 n = LittleLong(*in);
3846 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3847 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3848 *out = loadmodel->brushq3.data_brushes + n;
3852 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3858 in = (void *)(mod_base + l->fileofs);
3859 if (l->filelen % sizeof(*in))
3860 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3861 count = l->filelen / sizeof(*in);
3862 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3864 loadmodel->brushq3.data_leaffaces = out;
3865 loadmodel->brushq3.num_leaffaces = count;
3867 for (i = 0;i < count;i++, in++, out++)
3869 n = LittleLong(*in);
3870 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3871 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3872 *out = loadmodel->brushq3.data_faces + n;
3876 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3880 int i, j, n, c, count;
3882 in = (void *)(mod_base + l->fileofs);
3883 if (l->filelen % sizeof(*in))
3884 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3885 count = l->filelen / sizeof(*in);
3886 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3888 loadmodel->brushq3.data_leafs = out;
3889 loadmodel->brushq3.num_leafs = count;
3891 for (i = 0;i < count;i++, in++, out++)
3893 out->isnode = false;
3895 out->clusterindex = LittleLong(in->clusterindex);
3896 out->areaindex = LittleLong(in->areaindex);
3897 for (j = 0;j < 3;j++)
3899 // yes the mins/maxs are ints
3900 out->mins[j] = LittleLong(in->mins[j]);
3901 out->maxs[j] = LittleLong(in->maxs[j]);
3903 n = LittleLong(in->firstleafface);
3904 c = LittleLong(in->numleaffaces);
3905 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3906 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3907 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3908 out->numleaffaces = c;
3909 n = LittleLong(in->firstleafbrush);
3910 c = LittleLong(in->numleafbrushes);
3911 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3912 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3913 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3914 out->numleafbrushes = c;
3918 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3921 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3922 node->parent = parent;
3925 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3926 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3930 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3936 in = (void *)(mod_base + l->fileofs);
3937 if (l->filelen % sizeof(*in))
3938 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3939 count = l->filelen / sizeof(*in);
3940 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3942 loadmodel->brushq3.data_nodes = out;
3943 loadmodel->brushq3.num_nodes = count;
3945 for (i = 0;i < count;i++, in++, out++)
3949 n = LittleLong(in->planeindex);
3950 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3951 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3952 out->plane = loadmodel->brushq3.data_planes + n;
3953 for (j = 0;j < 2;j++)
3955 n = LittleLong(in->childrenindex[j]);
3958 if (n >= loadmodel->brushq3.num_nodes)
3959 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3960 out->children[j] = loadmodel->brushq3.data_nodes + n;
3965 if (n >= loadmodel->brushq3.num_leafs)
3966 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3967 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3970 for (j = 0;j < 3;j++)
3972 // yes the mins/maxs are ints
3973 out->mins[j] = LittleLong(in->mins[j]);
3974 out->maxs[j] = LittleLong(in->maxs[j]);
3978 // set the parent pointers
3979 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3982 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3985 q3dlightgrid_t *out;
3988 if (l->filelen == 0)
3991 in = (void *)(mod_base + l->fileofs);
3992 if (l->filelen % sizeof(*in))
3993 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
3994 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
3995 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
3996 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
3997 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3998 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3999 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4000 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4001 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4002 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4003 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4004 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4005 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4006 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4007 if (l->filelen < count * (int)sizeof(*in))
4008 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]);
4009 if (l->filelen != count * (int)sizeof(*in))
4010 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4012 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4013 loadmodel->brushq3.data_lightgrid = out;
4014 loadmodel->brushq3.num_lightgrid = count;
4016 // no swapping or validation necessary
4017 memcpy(out, in, count * (int)sizeof(*out));
4019 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]);
4020 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]);
4023 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4028 if (l->filelen == 0)
4031 in = (void *)(mod_base + l->fileofs);
4033 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4035 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4036 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4037 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4038 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4039 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4040 if (l->filelen < totalchains + (int)sizeof(*in))
4041 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);
4043 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4044 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4047 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4049 // FIXME: finish this code
4050 VectorCopy(in, out);
4053 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4055 int i, j, k, index[3];
4056 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4057 q3dlightgrid_t *a, *s;
4058 // FIXME: write this
4059 if (!model->brushq3.num_lightgrid)
4061 ambientcolor[0] += 128;
4062 ambientcolor[1] += 128;
4063 ambientcolor[2] += 128;
4066 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4067 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4068 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4069 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4070 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4071 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4072 index[0] = (int)floor(transformed[0]);
4073 index[1] = (int)floor(transformed[1]);
4074 index[2] = (int)floor(transformed[2]);
4075 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4076 // now lerp the values
4077 VectorClear(diffusenormal);
4078 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4079 for (k = 0;k < 2;k++)
4081 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4082 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4084 for (j = 0;j < 2;j++)
4086 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4087 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4089 for (i = 0;i < 2;i++)
4091 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4092 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4094 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4095 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4096 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4097 pitch = s->diffusepitch * M_PI / 128;
4098 yaw = s->diffuseyaw * M_PI / 128;
4099 sinpitch = sin(pitch);
4100 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4101 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4102 diffusenormal[2] += blend * (cos(pitch));
4103 //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)));
4107 VectorNormalize(diffusenormal);
4108 //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]);
4111 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)
4113 int i, startside, endside;
4114 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4118 if (startfrac >= trace->fraction)
4120 // note: all line fragments past first impact fraction are ignored
4121 while (node->isnode)
4123 // recurse down node sides
4124 dist1 = PlaneDiff(start, node->plane);
4125 dist2 = PlaneDiff(end, node->plane);
4126 startside = dist1 < 0;
4127 endside = dist2 < 0;
4128 if (startside == endside)
4130 // most of the time the line fragment is on one side of the plane
4131 node = node->children[startside];
4135 // line crosses node plane, split the line
4136 midfrac = dist1 / (dist1 - dist2);
4137 VectorLerp(linestart, midfrac, lineend, mid);
4138 // take the near side first
4139 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4140 if (midfrac < trace->fraction)
4141 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4146 segmentmins[0] = min(start[0], end[0]);
4147 segmentmins[1] = min(start[1], end[1]);
4148 segmentmins[2] = min(start[2], end[2]);
4149 segmentmaxs[0] = max(start[0], end[0]);
4150 segmentmaxs[1] = max(start[1], end[1]);
4151 segmentmaxs[2] = max(start[2], end[2]);
4152 leaf = (q3mleaf_t *)node;
4153 for (i = 0;i < leaf->numleafbrushes;i++)
4155 if (startfrac >= trace->fraction)
4157 brush = leaf->firstleafbrush[i]->colbrushf;
4158 if (brush && brush->markframe != markframe)
4160 brush->markframe = markframe;
4161 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4162 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4165 if (mod_q3bsp_curves_collisions.integer)
4167 for (i = 0;i < leaf->numleaffaces;i++)
4169 if (startfrac >= trace->fraction)
4171 face = leaf->firstleafface[i];
4172 if (face->collisions && face->collisionmarkframe != markframe)
4174 face->collisionmarkframe = markframe;
4175 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4176 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4182 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)
4185 float nodesegmentmins[3], nodesegmentmaxs[3];
4189 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4190 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4191 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4192 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4193 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4194 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4195 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4199 // recurse down node sides
4200 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4203 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4204 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4206 else if (sides == 2)
4207 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4209 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4211 dist = node->plane->dist - (1.0f / 8.0f);
4212 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4214 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4216 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4222 dist = node->plane->dist + (1.0f / 8.0f);
4223 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4225 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4227 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4233 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4235 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4237 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4243 leaf = (q3mleaf_t *)node;
4244 for (i = 0;i < leaf->numleafbrushes;i++)
4246 brush = leaf->firstleafbrush[i]->colbrushf;
4247 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4249 brush->markframe = markframe;
4250 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4253 if (mod_q3bsp_curves_collisions.integer)
4255 for (i = 0;i < leaf->numleaffaces;i++)
4257 face = leaf->firstleafface[i];
4258 // 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
4259 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4260 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4266 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)
4269 float segmentmins[3], segmentmaxs[3];
4270 colbrushf_t *thisbrush_start, *thisbrush_end;
4271 matrix4x4_t startmatrix, endmatrix;
4272 static int markframe = 0;
4274 memset(trace, 0, sizeof(*trace));
4275 trace->fraction = 1;
4276 trace->hitsupercontentsmask = hitsupercontentsmask;
4277 Matrix4x4_CreateIdentity(&startmatrix);
4278 Matrix4x4_CreateIdentity(&endmatrix);
4279 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4280 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4281 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4282 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4283 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4284 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4285 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4288 if (model->brushq3.submodel)
4290 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4291 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4292 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4293 if (mod_q3bsp_curves_collisions.integer)
4295 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4297 face = model->brushq3.data_thismodel->firstface + i;
4298 if (face->collisions)
4299 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4304 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4308 // box trace, performed as brush trace
4309 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4310 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4311 if (model->brushq3.submodel)
4313 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4314 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4315 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4316 if (mod_q3bsp_curves_collisions.integer)
4318 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4320 face = model->brushq3.data_thismodel->firstface + i;
4321 if (face->collisions)
4322 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4327 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4332 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)
4339 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4340 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4343 // node - recurse down the BSP tree
4344 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4347 node = node->children[0];
4350 node = node->children[1];
4352 default: // crossing
4353 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4355 node = node->children[1];
4362 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4364 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4367 //Returns PVS data for a given point
4368 //(note: can return NULL)
4369 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4372 Mod_CheckLoaded(model);
4373 node = model->brushq3.data_nodes;
4374 while (node->isnode)
4375 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4376 if (((q3mleaf_t *)node)->clusterindex >= 0)
4377 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4382 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4388 while (node->isnode)
4390 d = PlaneDiff(org, node->plane);
4392 node = node->children[0];
4393 else if (d < -radius)
4394 node = node->children[1];
4397 // go down both sides
4398 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4399 node = node->children[1];
4402 // if this is a leaf with a pvs, accumulate the pvs bits
4403 if (((q3mleaf_t *)node)->clusterindex >= 0)
4405 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4406 for (i = 0;i < pvsbytes;i++)
4407 pvsbuffer[i] |= pvs[i];
4412 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4413 //of the given point.
4414 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4416 int bytes = model->brushq3.num_pvschainlength;
4417 bytes = min(bytes, pvsbufferlength);
4418 memset(pvsbuffer, 0, bytes);
4419 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4424 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4426 int supercontents = 0;
4427 if (nativecontents & Q2CONTENTS_SOLID)
4428 supercontents |= SUPERCONTENTS_SOLID;
4429 if (nativecontents & Q2CONTENTS_WATER)
4430 supercontents |= SUPERCONTENTS_WATER;
4431 if (nativecontents & Q2CONTENTS_SLIME)
4432 supercontents |= SUPERCONTENTS_SLIME;
4433 if (nativecontents & Q2CONTENTS_LAVA)
4434 supercontents |= SUPERCONTENTS_LAVA;
4435 return supercontents;
4438 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4440 int nativecontents = 0;
4441 if (supercontents & SUPERCONTENTS_SOLID)
4442 nativecontents |= Q2CONTENTS_SOLID;
4443 if (supercontents & SUPERCONTENTS_WATER)
4444 nativecontents |= Q2CONTENTS_WATER;
4445 if (supercontents & SUPERCONTENTS_SLIME)
4446 nativecontents |= Q2CONTENTS_SLIME;
4447 if (supercontents & SUPERCONTENTS_LAVA)
4448 nativecontents |= Q2CONTENTS_LAVA;
4449 return nativecontents;
4452 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4453 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4454 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4455 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);
4456 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4459 q3dheader_t *header;
4460 float corner[3], yawradius, modelradius;
4462 mod->type = mod_brushq3;
4466 header = (q3dheader_t *)buffer;
4468 i = LittleLong(header->version);
4469 if (i != Q3BSPVERSION)
4470 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4471 if (loadmodel->isworldmodel)
4473 Cvar_SetValue("halflifebsp", false);
4474 // until we get a texture for it...
4478 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4479 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4480 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4481 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4482 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4483 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4484 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4485 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4486 //mod->DrawSky = R_Q3BSP_DrawSky;
4487 mod->Draw = R_Q3BSP_Draw;
4488 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4489 mod->DrawLight = R_Q3BSP_DrawLight;
4491 mod_base = (qbyte *)header;
4493 // swap all the lumps
4494 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4495 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4497 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4498 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4499 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4500 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4501 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4502 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4503 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4504 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4505 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4506 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4507 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4508 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4509 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4510 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4511 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4512 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4513 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4514 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4516 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4523 // LordHavoc: only register submodels if it is the world
4524 // (prevents bsp models from replacing world submodels)
4525 if (!loadmodel->isworldmodel)
4527 // duplicate the basic information
4528 sprintf(name, "*%i", i);
4529 mod = Mod_FindName(name);
4531 strcpy(mod->name, name);
4532 // textures and memory belong to the main model
4533 mod->texturepool = NULL;
4534 mod->mempool = NULL;
4536 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4537 mod->brushq3.submodel = i;
4539 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4540 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4541 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4542 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4543 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4544 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4545 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4546 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4547 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4548 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4549 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4550 mod->yawmins[2] = mod->normalmins[2];
4551 mod->yawmaxs[2] = mod->normalmaxs[2];
4552 mod->radius = modelradius;
4553 mod->radius2 = modelradius * modelradius;
4557 void Mod_IBSP_Load(model_t *mod, void *buffer)
4559 int i = LittleLong(((int *)buffer)[1]);
4560 if (i == Q3BSPVERSION)
4561 Mod_Q3BSP_Load(mod,buffer);
4562 else if (i == Q2BSPVERSION)
4563 Mod_Q2BSP_Load(mod,buffer);
4565 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4568 void Mod_MAP_Load(model_t *mod, void *buffer)
4570 Host_Error("Mod_MAP_Load: not yet implemented\n");