2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
38 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
39 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
41 void Mod_BrushInit(void)
43 // Cvar_RegisterVariable(&r_subdivide_size);
44 Cvar_RegisterVariable(&halflifebsp);
45 Cvar_RegisterVariable(&r_novis);
46 Cvar_RegisterVariable(&r_miplightmaps);
47 Cvar_RegisterVariable(&r_lightmaprgba);
48 Cvar_RegisterVariable(&r_nosurftextures);
49 Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
50 Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
51 Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
52 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
62 Mod_CheckLoaded(model);
64 // LordHavoc: modified to start at first clip node,
65 // in other words: first node of the (sub)model
66 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
67 while (node->contents == 0)
68 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
70 return (mleaf_t *)node;
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
77 leaf = Mod_Q1BSP_PointInLeaf(model, p);
80 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
83 memcpy(out, leaf->ambient_sound_level, i);
89 memset(out, 0, outsize);
93 static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
97 if (node->contents < 0)
100 if (node->contents == CONTENTS_SOLID)
102 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
106 // node - recurse down the BSP tree
107 switch (BoxOnPlaneSide(mins, maxs, node->plane))
110 node = node->children[0];
113 node = node->children[1];
116 if (node->children[0]->contents != CONTENTS_SOLID)
117 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
119 node = node->children[1];
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
128 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
137 return CONTENTS_EMPTY;
139 Mod_CheckLoaded(model);
141 // LordHavoc: modified to start at first clip node,
142 // in other words: first node of the (sub)model
143 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
144 while (node->contents == 0)
145 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
147 return ((mleaf_t *)node)->contents;
151 typedef struct findnonsolidlocationinfo_s
159 findnonsolidlocationinfo_t;
162 extern cvar_t samelevel;
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
166 int i, surfnum, k, *tri, *mark;
167 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
173 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
175 surf = info->model->brushq1.surfaces + *mark;
176 if (surf->flags & SURF_SOLIDCLIP)
179 VectorCopy(surf->plane->normal, surfnormal);
180 if (surf->flags & SURF_PLANEBACK)
181 VectorNegate(surfnormal, surfnormal);
183 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
185 for (k = 0;k < mesh->numtriangles;k++)
187 tri = mesh->element3i + k * 3;
188 VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
189 VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
190 VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
191 VectorSubtract(vert[1], vert[0], edge[0]);
192 VectorSubtract(vert[2], vert[1], edge[1]);
193 CrossProduct(edge[1], edge[0], facenormal);
194 if (facenormal[0] || facenormal[1] || facenormal[2])
196 VectorNormalize(facenormal);
198 if (VectorDistance(facenormal, surfnormal) > 0.01f)
199 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
201 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
202 if (f <= info->bestdist && f >= -info->bestdist)
204 VectorSubtract(vert[0], vert[2], edge[2]);
205 VectorNormalize(edge[0]);
206 VectorNormalize(edge[1]);
207 VectorNormalize(edge[2]);
208 CrossProduct(facenormal, edge[0], edgenormal[0]);
209 CrossProduct(facenormal, edge[1], edgenormal[1]);
210 CrossProduct(facenormal, edge[2], edgenormal[2]);
212 if (samelevel.integer & 1)
213 VectorNegate(edgenormal[0], edgenormal[0]);
214 if (samelevel.integer & 2)
215 VectorNegate(edgenormal[1], edgenormal[1]);
216 if (samelevel.integer & 4)
217 VectorNegate(edgenormal[2], edgenormal[2]);
218 for (i = 0;i < 3;i++)
219 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
220 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
221 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
222 Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]);
225 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
226 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
227 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
229 // we got lucky, the center is within the face
230 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
234 if (info->bestdist > dist)
236 info->bestdist = dist;
237 VectorScale(facenormal, (info->radius - -dist), info->nudge);
242 if (info->bestdist > dist)
244 info->bestdist = dist;
245 VectorScale(facenormal, (info->radius - dist), info->nudge);
251 // check which edge or vertex the center is nearest
252 for (i = 0;i < 3;i++)
254 f = DotProduct(info->center, edge[i]);
255 if (f >= DotProduct(vert[0], edge[i])
256 && f <= DotProduct(vert[1], edge[i]))
259 VectorMA(info->center, -f, edge[i], point);
260 dist = sqrt(DotProduct(point, point));
261 if (info->bestdist > dist)
263 info->bestdist = dist;
264 VectorScale(point, (info->radius / dist), info->nudge);
266 // skip both vertex checks
267 // (both are further away than this edge)
272 // not on edge, check first vertex of edge
273 VectorSubtract(info->center, vert[i], point);
274 dist = sqrt(DotProduct(point, point));
275 if (info->bestdist > dist)
277 info->bestdist = dist;
278 VectorScale(point, (info->radius / dist), info->nudge);
291 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
295 if (((mleaf_t *)node)->nummarksurfaces)
296 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
300 float f = PlaneDiff(info->center, node->plane);
301 if (f >= -info->bestdist)
302 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
303 if (f <= info->bestdist)
304 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
308 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
311 findnonsolidlocationinfo_t info;
317 VectorCopy(in, info.center);
318 info.radius = radius;
323 VectorClear(info.nudge);
324 info.bestdist = radius;
325 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
326 VectorAdd(info.center, info.nudge, info.center);
328 while (info.bestdist < radius && ++i < 10);
329 VectorCopy(info.center, out);
332 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
334 switch(nativecontents)
339 return SUPERCONTENTS_SOLID;
341 return SUPERCONTENTS_WATER;
343 return SUPERCONTENTS_SLIME;
345 return SUPERCONTENTS_LAVA;
347 return SUPERCONTENTS_SKY;
352 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
354 if (supercontents & SUPERCONTENTS_SOLID)
355 return CONTENTS_SOLID;
356 if (supercontents & SUPERCONTENTS_SKY)
358 if (supercontents & SUPERCONTENTS_LAVA)
359 return CONTENTS_LAVA;
360 if (supercontents & SUPERCONTENTS_SLIME)
361 return CONTENTS_SLIME;
362 if (supercontents & SUPERCONTENTS_WATER)
363 return CONTENTS_WATER;
364 return CONTENTS_EMPTY;
369 // the hull we're tracing through
372 // the trace structure to fill in
375 // start, end, and end - start (in model space)
380 RecursiveHullCheckTraceInfo_t;
382 // 1/32 epsilon to keep floating point happy
383 #define DIST_EPSILON (0.03125)
385 #define HULLCHECKSTATE_EMPTY 0
386 #define HULLCHECKSTATE_SOLID 1
387 #define HULLCHECKSTATE_DONE 2
389 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
391 // status variables, these don't need to be saved on the stack when
392 // recursing... but are because this should be thread-safe
393 // (note: tracing against a bbox is not thread-safe, yet)
398 // variables that need to be stored on the stack when recursing
403 // LordHavoc: a goto! everyone flee in terror... :)
408 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
409 if (!t->trace->startfound)
411 t->trace->startfound = true;
412 t->trace->startsupercontents |= num;
414 if (num & SUPERCONTENTS_LIQUIDSMASK)
415 t->trace->inwater = true;
417 t->trace->inopen = true;
418 if (num & t->trace->hitsupercontentsmask)
420 // if the first leaf is solid, set startsolid
421 if (t->trace->allsolid)
422 t->trace->startsolid = true;
423 return HULLCHECKSTATE_SOLID;
427 t->trace->allsolid = false;
428 return HULLCHECKSTATE_EMPTY;
432 // find the point distances
433 node = t->hull->clipnodes + num;
435 plane = t->hull->planes + node->planenum;
438 t1 = p1[plane->type] - plane->dist;
439 t2 = p2[plane->type] - plane->dist;
443 t1 = DotProduct (plane->normal, p1) - plane->dist;
444 t2 = DotProduct (plane->normal, p2) - plane->dist;
451 num = node->children[1];
460 num = node->children[0];
466 // the line intersects, find intersection point
467 // LordHavoc: this uses the original trace for maximum accuracy
470 t1 = t->start[plane->type] - plane->dist;
471 t2 = t->end[plane->type] - plane->dist;
475 t1 = DotProduct (plane->normal, t->start) - plane->dist;
476 t2 = DotProduct (plane->normal, t->end) - plane->dist;
479 midf = t1 / (t1 - t2);
480 midf = bound(p1f, midf, p2f);
481 VectorMA(t->start, midf, t->dist, mid);
483 // recurse both sides, front side first
484 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
485 // if this side is not empty, return what it is (solid or done)
486 if (ret != HULLCHECKSTATE_EMPTY)
489 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
490 // if other side is not solid, return what it is (empty or done)
491 if (ret != HULLCHECKSTATE_SOLID)
494 // front is air and back is solid, this is the impact point...
497 t->trace->plane.dist = -plane->dist;
498 VectorNegate (plane->normal, t->trace->plane.normal);
502 t->trace->plane.dist = plane->dist;
503 VectorCopy (plane->normal, t->trace->plane.normal);
506 // bias away from surface a bit
507 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
508 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
510 midf = t1 / (t1 - t2);
511 t->trace->fraction = bound(0.0f, midf, 1.0);
513 return HULLCHECKSTATE_DONE;
516 static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
518 // this function currently only supports same size start and end
520 RecursiveHullCheckTraceInfo_t rhc;
522 memset(&rhc, 0, sizeof(rhc));
523 memset(trace, 0, sizeof(trace_t));
525 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
526 rhc.trace->fraction = 1;
527 rhc.trace->allsolid = true;
528 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
530 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
531 else if (model->brush.ishlbsp)
533 if (boxsize[0] <= 32)
535 if (boxsize[2] < 54) // pick the nearest of 36 or 72
536 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
538 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
541 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
545 if (boxsize[0] <= 32)
546 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
548 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
550 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
551 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
552 VectorSubtract(rhc.end, rhc.start, rhc.dist);
553 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
556 static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
558 int side, distz = endz - startz;
563 if (node->contents < 0)
564 return false; // didn't hit anything
566 switch (node->plane->type)
569 node = node->children[x < node->plane->dist];
572 node = node->children[y < node->plane->dist];
575 side = startz < node->plane->dist;
576 if ((endz < node->plane->dist) == side)
578 node = node->children[side];
581 // found an intersection
582 mid = node->plane->dist;
585 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
586 front += startz * node->plane->normal[2];
587 back += endz * node->plane->normal[2];
588 side = front < node->plane->dist;
589 if ((back < node->plane->dist) == side)
591 node = node->children[side];
594 // found an intersection
595 mid = startz + distz * (front - node->plane->dist) / (front - back);
599 // go down front side
600 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
601 return true; // hit something
604 // check for impact on this node
605 if (node->numsurfaces)
610 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
611 for (i = 0;i < node->numsurfaces;i++, surf++)
613 if (!(surf->flags & SURF_LIGHTMAP))
614 continue; // no lightmaps
616 ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3]);
617 dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]);
619 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
622 ds -= surf->texturemins[0];
623 dt -= surf->texturemins[1];
625 if (ds > surf->extents[0] || dt > surf->extents[1])
631 int maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
632 line3 = ((surf->extents[0]>>4)+1)*3;
633 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
635 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
637 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
639 scale = d_lightstylevalue[surf->styles[maps]];
640 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
641 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
642 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
643 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
648 LordHavoc: here's the readable version of the interpolation
649 code, not quite as easy for the compiler to optimize...
651 dsfrac is the X position in the lightmap pixel, * 16
652 dtfrac is the Y position in the lightmap pixel, * 16
653 r00 is top left corner, r01 is top right corner
654 r10 is bottom left corner, r11 is bottom right corner
655 g and b are the same layout.
656 r0 and r1 are the top and bottom intermediate results
658 first we interpolate the top two points, to get the top
661 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
662 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
663 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
665 then we interpolate the bottom two points, to get the
668 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
669 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
670 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
672 then we interpolate the top and bottom samples to get the
673 middle sample (the one which was requested)
675 r = (((r1-r0) * dtfrac) >> 4) + r0;
676 g = (((g1-g0) * dtfrac) >> 4) + g0;
677 b = (((b1-b0) * dtfrac) >> 4) + b0;
680 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
681 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
682 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
684 return true; // success
689 node = node->children[side ^ 1];
691 distz = endz - startz;
696 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
698 Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, cl.worldmodel->brushq1.nodes + cl.worldmodel->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
701 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
708 Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
716 for (c = *in++;c > 0;c--)
720 Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
729 static void Mod_Q1BSP_LoadTextures(lump_t *l)
731 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
733 texture_t *tx, *tx2, *anims[10], *altanims[10];
735 qbyte *data, *mtdata;
738 loadmodel->brushq1.textures = NULL;
740 // add two slots for notexture walls and notexture liquids
743 m = (dmiptexlump_t *)(mod_base + l->fileofs);
744 m->nummiptex = LittleLong (m->nummiptex);
745 loadmodel->brushq1.numtextures = m->nummiptex + 2;
750 loadmodel->brushq1.numtextures = 2;
753 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
755 // fill out all slots with notexture
756 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
759 strcpy(tx->name, "NO TEXTURE FOUND");
762 tx->skin.base = r_notexture;
763 tx->shader = &Cshader_wall_lightmap;
764 tx->flags = SURF_SOLIDCLIP;
765 if (i == loadmodel->brushq1.numtextures - 1)
767 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
768 tx->shader = &Cshader_water;
770 tx->currentframe = tx;
776 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
778 // LordHavoc: mostly rewritten map texture loader
779 for (i = 0;i < m->nummiptex;i++)
781 dofs[i] = LittleLong(dofs[i]);
782 if (dofs[i] == -1 || r_nosurftextures.integer)
784 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
786 // make sure name is no more than 15 characters
787 for (j = 0;dmiptex->name[j] && j < 15;j++)
788 name[j] = dmiptex->name[j];
791 mtwidth = LittleLong(dmiptex->width);
792 mtheight = LittleLong(dmiptex->height);
794 j = LittleLong(dmiptex->offsets[0]);
798 if (j < 40 || j + mtwidth * mtheight > l->filelen)
800 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
803 mtdata = (qbyte *)dmiptex + j;
806 if ((mtwidth & 15) || (mtheight & 15))
807 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
809 // LordHavoc: force all names to lowercase
810 for (j = 0;name[j];j++)
811 if (name[j] >= 'A' && name[j] <= 'Z')
812 name[j] += 'a' - 'A';
814 tx = loadmodel->brushq1.textures + i;
815 strcpy(tx->name, name);
817 tx->height = mtheight;
821 sprintf(tx->name, "unnamed%i", i);
822 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
825 // LordHavoc: HL sky textures are entirely different than quake
826 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
828 if (loadmodel->isworldmodel)
830 data = loadimagepixels(tx->name, false, 0, 0);
833 if (image_width == 256 && image_height == 128)
841 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
843 R_InitSky(mtdata, 1);
846 else if (mtdata != NULL)
847 R_InitSky(mtdata, 1);
852 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
854 // did not find external texture, load it from the bsp or wad3
855 if (loadmodel->brush.ishlbsp)
857 // internal texture overrides wad
858 qbyte *pixels, *freepixels, *fogpixels;
859 pixels = freepixels = NULL;
861 pixels = W_ConvertWAD3Texture(dmiptex);
863 pixels = freepixels = W_GetTexture(tx->name);
866 tx->width = image_width;
867 tx->height = image_height;
868 tx->skin.base = tx->skin.merged = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
869 if (Image_CheckAlpha(pixels, image_width * image_height, true))
871 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
872 for (j = 0;j < image_width * image_height * 4;j += 4)
874 fogpixels[j + 0] = 255;
875 fogpixels[j + 1] = 255;
876 fogpixels[j + 2] = 255;
877 fogpixels[j + 3] = pixels[j + 3];
879 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
884 Mem_Free(freepixels);
886 else if (mtdata) // texture included
887 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
890 if (tx->skin.base == NULL)
895 tx->skin.base = r_notexture;
898 if (tx->name[0] == '*')
900 // turb does not block movement
901 tx->flags &= ~SURF_SOLIDCLIP;
902 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
903 // LordHavoc: some turbulent textures should be fullbright and solid
904 if (!strncmp(tx->name,"*lava",5)
905 || !strncmp(tx->name,"*teleport",9)
906 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
907 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
909 tx->flags |= SURF_WATERALPHA;
910 tx->shader = &Cshader_water;
912 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
914 tx->flags |= SURF_DRAWSKY;
915 tx->shader = &Cshader_sky;
919 tx->flags |= SURF_LIGHTMAP;
921 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
922 tx->shader = &Cshader_wall_lightmap;
925 // start out with no animation
926 tx->currentframe = tx;
929 // sequence the animations
930 for (i = 0;i < m->nummiptex;i++)
932 tx = loadmodel->brushq1.textures + i;
933 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
935 if (tx->anim_total[0] || tx->anim_total[1])
936 continue; // already sequenced
938 // find the number of frames in the animation
939 memset(anims, 0, sizeof(anims));
940 memset(altanims, 0, sizeof(altanims));
942 for (j = i;j < m->nummiptex;j++)
944 tx2 = loadmodel->brushq1.textures + j;
945 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
949 if (num >= '0' && num <= '9')
950 anims[num - '0'] = tx2;
951 else if (num >= 'a' && num <= 'j')
952 altanims[num - 'a'] = tx2;
954 Con_Printf("Bad animating texture %s\n", tx->name);
958 for (j = 0;j < 10;j++)
965 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
968 for (j = 0;j < max;j++)
972 Con_Printf("Missing frame %i of %s\n", j, tx->name);
976 for (j = 0;j < altmax;j++)
980 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
989 // if there is no alternate animation, duplicate the primary
990 // animation into the alternate
992 for (k = 0;k < 10;k++)
993 altanims[k] = anims[k];
996 // link together the primary animation
997 for (j = 0;j < max;j++)
1000 tx2->animated = true;
1001 tx2->anim_total[0] = max;
1002 tx2->anim_total[1] = altmax;
1003 for (k = 0;k < 10;k++)
1005 tx2->anim_frames[0][k] = anims[k];
1006 tx2->anim_frames[1][k] = altanims[k];
1010 // if there really is an alternate anim...
1011 if (anims[0] != altanims[0])
1013 // link together the alternate animation
1014 for (j = 0;j < altmax;j++)
1017 tx2->animated = true;
1018 // the primary/alternate are reversed here
1019 tx2->anim_total[0] = altmax;
1020 tx2->anim_total[1] = max;
1021 for (k = 0;k < 10;k++)
1023 tx2->anim_frames[0][k] = altanims[k];
1024 tx2->anim_frames[1][k] = anims[k];
1031 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1034 qbyte *in, *out, *data, d;
1035 char litfilename[1024];
1036 loadmodel->brushq1.lightdata = NULL;
1037 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1039 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1040 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1042 else // LordHavoc: bsp version 29 (normal white lighting)
1044 // LordHavoc: hope is not lost yet, check for a .lit file to load
1045 strcpy(litfilename, loadmodel->name);
1046 FS_StripExtension(litfilename, litfilename);
1047 strcat(litfilename, ".lit");
1048 data = (qbyte*) FS_LoadFile(litfilename, false);
1051 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1053 i = LittleLong(((int *)data)[1]);
1056 Con_DPrintf("loaded %s\n", litfilename);
1057 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1058 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1064 Con_Printf("Unknown .lit file version (%d)\n", i);
1070 if (fs_filesize == 8)
1071 Con_Printf("Empty .lit file, ignoring\n");
1073 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1077 // LordHavoc: oh well, expand the white lighting data
1080 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1081 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1082 out = loadmodel->brushq1.lightdata;
1083 memcpy(in, mod_base + l->fileofs, l->filelen);
1084 for (i = 0;i < l->filelen;i++)
1094 static void Mod_Q1BSP_LoadLightList(void)
1096 int a, n, numlights;
1097 char lightsfilename[1024], *s, *t, *lightsstring;
1100 strcpy(lightsfilename, loadmodel->name);
1101 FS_StripExtension(lightsfilename, lightsfilename);
1102 strcat(lightsfilename, ".lights");
1103 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1109 while (*s && *s != '\n')
1113 Mem_Free(lightsstring);
1114 Host_Error("lights file must end with a newline\n");
1119 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1122 while (*s && n < numlights)
1125 while (*s && *s != '\n')
1129 Mem_Free(lightsstring);
1130 Host_Error("misparsed lights file!\n");
1132 e = loadmodel->brushq1.lights + n;
1134 a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
1138 Mem_Free(lightsstring);
1139 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
1146 Mem_Free(lightsstring);
1147 Host_Error("misparsed lights file!\n");
1149 loadmodel->brushq1.numlights = numlights;
1150 Mem_Free(lightsstring);
1154 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1156 loadmodel->brushq1.num_compressedpvs = 0;
1157 loadmodel->brushq1.data_compressedpvs = NULL;
1160 loadmodel->brushq1.num_compressedpvs = l->filelen;
1161 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1162 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1165 // used only for HalfLife maps
1166 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1168 char key[128], value[4096];
1173 if (!COM_ParseToken(&data, false))
1175 if (com_token[0] != '{')
1179 if (!COM_ParseToken(&data, false))
1181 if (com_token[0] == '}')
1182 break; // end of worldspawn
1183 if (com_token[0] == '_')
1184 strcpy(key, com_token + 1);
1186 strcpy(key, com_token);
1187 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1188 key[strlen(key)-1] = 0;
1189 if (!COM_ParseToken(&data, false))
1191 strcpy(value, com_token);
1192 if (!strcmp("wad", key)) // for HalfLife maps
1194 if (loadmodel->brush.ishlbsp)
1197 for (i = 0;i < 4096;i++)
1198 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1204 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1205 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1207 else if (value[i] == ';' || value[i] == 0)
1211 strcpy(wadname, "textures/");
1212 strcat(wadname, &value[j]);
1213 W_LoadTextureWadFile(wadname, false);
1225 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1227 loadmodel->brush.entities = NULL;
1230 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1231 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1232 if (loadmodel->brush.ishlbsp)
1233 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1237 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1243 in = (void *)(mod_base + l->fileofs);
1244 if (l->filelen % sizeof(*in))
1245 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1246 count = l->filelen / sizeof(*in);
1247 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1249 loadmodel->brushq1.vertexes = out;
1250 loadmodel->brushq1.numvertexes = count;
1252 for ( i=0 ; i<count ; i++, in++, out++)
1254 out->position[0] = LittleFloat(in->point[0]);
1255 out->position[1] = LittleFloat(in->point[1]);
1256 out->position[2] = LittleFloat(in->point[2]);
1260 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1266 in = (void *)(mod_base + l->fileofs);
1267 if (l->filelen % sizeof(*in))
1268 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1269 count = l->filelen / sizeof(*in);
1270 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1272 loadmodel->brushq1.submodels = out;
1273 loadmodel->brush.numsubmodels = count;
1275 for ( i=0 ; i<count ; i++, in++, out++)
1277 for (j=0 ; j<3 ; j++)
1279 // spread the mins / maxs by a pixel
1280 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1281 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1282 out->origin[j] = LittleFloat(in->origin[j]);
1284 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1285 out->headnode[j] = LittleLong(in->headnode[j]);
1286 out->visleafs = LittleLong(in->visleafs);
1287 out->firstface = LittleLong(in->firstface);
1288 out->numfaces = LittleLong(in->numfaces);
1292 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1298 in = (void *)(mod_base + l->fileofs);
1299 if (l->filelen % sizeof(*in))
1300 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1301 count = l->filelen / sizeof(*in);
1302 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1304 loadmodel->brushq1.edges = out;
1305 loadmodel->brushq1.numedges = count;
1307 for ( i=0 ; i<count ; i++, in++, out++)
1309 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1310 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1314 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1318 int i, j, k, count, miptex;
1320 in = (void *)(mod_base + l->fileofs);
1321 if (l->filelen % sizeof(*in))
1322 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1323 count = l->filelen / sizeof(*in);
1324 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1326 loadmodel->brushq1.texinfo = out;
1327 loadmodel->brushq1.numtexinfo = count;
1329 for (i = 0;i < count;i++, in++, out++)
1331 for (k = 0;k < 2;k++)
1332 for (j = 0;j < 4;j++)
1333 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1335 miptex = LittleLong(in->miptex);
1336 out->flags = LittleLong(in->flags);
1338 out->texture = NULL;
1339 if (loadmodel->brushq1.textures)
1341 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1342 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1344 out->texture = loadmodel->brushq1.textures + miptex;
1346 if (out->flags & TEX_SPECIAL)
1348 // if texture chosen is NULL or the shader needs a lightmap,
1349 // force to notexture water shader
1350 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1351 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1355 // if texture chosen is NULL, force to notexture
1356 if (out->texture == NULL)
1357 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1363 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1368 mins[0] = mins[1] = mins[2] = 9999;
1369 maxs[0] = maxs[1] = maxs[2] = -9999;
1371 for (i = 0;i < numverts;i++)
1373 for (j = 0;j < 3;j++, v++)
1383 #define MAX_SUBDIVPOLYTRIANGLES 4096
1384 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1386 static int subdivpolyverts, subdivpolytriangles;
1387 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1388 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1390 static int subdivpolylookupvert(vec3_t v)
1393 for (i = 0;i < subdivpolyverts;i++)
1394 if (subdivpolyvert[i][0] == v[0]
1395 && subdivpolyvert[i][1] == v[1]
1396 && subdivpolyvert[i][2] == v[2])
1398 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1399 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1400 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1401 return subdivpolyverts++;
1404 static void SubdividePolygon(int numverts, float *verts)
1406 int i, i1, i2, i3, f, b, c, p;
1407 vec3_t mins, maxs, front[256], back[256];
1408 float m, *pv, *cv, dist[256], frac;
1411 Host_Error("SubdividePolygon: ran out of verts in buffer");
1413 BoundPoly(numverts, verts, mins, maxs);
1415 for (i = 0;i < 3;i++)
1417 m = (mins[i] + maxs[i]) * 0.5;
1418 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1419 if (maxs[i] - m < 8)
1421 if (m - mins[i] < 8)
1425 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1426 dist[c] = cv[i] - m;
1429 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1433 VectorCopy(pv, front[f]);
1438 VectorCopy(pv, back[b]);
1441 if (dist[p] == 0 || dist[c] == 0)
1443 if ((dist[p] > 0) != (dist[c] > 0) )
1446 frac = dist[p] / (dist[p] - dist[c]);
1447 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1448 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1449 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1455 SubdividePolygon(f, front[0]);
1456 SubdividePolygon(b, back[0]);
1460 i1 = subdivpolylookupvert(verts);
1461 i2 = subdivpolylookupvert(verts + 3);
1462 for (i = 2;i < numverts;i++)
1464 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1466 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1470 i3 = subdivpolylookupvert(verts + i * 3);
1471 subdivpolyindex[subdivpolytriangles][0] = i1;
1472 subdivpolyindex[subdivpolytriangles][1] = i2;
1473 subdivpolyindex[subdivpolytriangles][2] = i3;
1475 subdivpolytriangles++;
1479 //Breaks a polygon up along axial 64 unit
1480 //boundaries so that turbulent and sky warps
1481 //can be done reasonably.
1482 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1488 subdivpolytriangles = 0;
1489 subdivpolyverts = 0;
1490 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1491 if (subdivpolytriangles < 1)
1492 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1494 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1495 mesh->numverts = subdivpolyverts;
1496 mesh->numtriangles = subdivpolytriangles;
1497 mesh->vertex = (surfvertex_t *)(mesh + 1);
1498 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1499 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1501 for (i = 0;i < mesh->numtriangles;i++)
1502 for (j = 0;j < 3;j++)
1503 mesh->index[i*3+j] = subdivpolyindex[i][j];
1505 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1507 VectorCopy(subdivpolyvert[i], v->v);
1508 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1509 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1514 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1517 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1518 mesh->numverts = numverts;
1519 mesh->numtriangles = numtriangles;
1520 mesh->vertex3f = (float *)(mesh + 1);
1521 mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1522 mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1523 mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1524 mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1525 mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1526 mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1527 mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1528 mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1529 mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1533 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1536 float *vec, *vert, mins[3], maxs[3], val, *v;
1539 // convert edges back to a normal polygon
1540 surf->poly_numverts = numedges;
1541 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1542 for (i = 0;i < numedges;i++)
1544 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1546 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1548 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1549 VectorCopy(vec, vert);
1553 // calculate polygon bounding box and center
1554 vert = surf->poly_verts;
1555 VectorCopy(vert, mins);
1556 VectorCopy(vert, maxs);
1558 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1560 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1561 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1562 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1564 VectorCopy(mins, surf->poly_mins);
1565 VectorCopy(maxs, surf->poly_maxs);
1566 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1567 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1568 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1570 // generate surface extents information
1571 tex = surf->texinfo;
1572 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1573 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1574 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1576 for (j = 0;j < 2;j++)
1578 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1585 for (i = 0;i < 2;i++)
1587 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1588 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1592 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1596 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1600 in = (void *)(mod_base + l->fileofs);
1601 if (l->filelen % sizeof(*in))
1602 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1603 count = l->filelen / sizeof(*in);
1604 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1606 loadmodel->brushq1.numsurfaces = count;
1607 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1608 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1609 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1611 for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, in++, surf++)
1613 surf->number = surfnum;
1614 // FIXME: validate edges, texinfo, etc?
1615 firstedge = LittleLong(in->firstedge);
1616 numedges = LittleShort(in->numedges);
1617 if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges)
1618 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1619 i = LittleShort(in->texinfo);
1620 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1621 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1622 surf->texinfo = loadmodel->brushq1.texinfo + i;
1623 surf->flags = surf->texinfo->texture->flags;
1625 planenum = LittleShort(in->planenum);
1626 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1627 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1629 if (LittleShort(in->side))
1630 surf->flags |= SURF_PLANEBACK;
1632 surf->plane = loadmodel->brushq1.planes + planenum;
1634 // clear lightmap (filled in later)
1635 surf->lightmaptexture = NULL;
1637 // force lightmap upload on first time seeing the surface
1638 surf->cached_dlight = true;
1640 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1642 ssize = (surf->extents[0] >> 4) + 1;
1643 tsize = (surf->extents[1] >> 4) + 1;
1646 for (i = 0;i < MAXLIGHTMAPS;i++)
1647 surf->styles[i] = in->styles[i];
1648 i = LittleLong(in->lightofs);
1650 surf->samples = NULL;
1651 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1652 surf->samples = loadmodel->brushq1.lightdata + i;
1653 else // LordHavoc: white lighting (bsp version 29)
1654 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1656 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1658 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1659 Host_Error("Bad surface extents");
1660 // stainmap for permanent marks on walls
1661 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1663 memset(surf->stainsamples, 255, ssize * tsize * 3);
1667 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1668 loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1670 for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++)
1672 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1673 mesh->numverts = surf->poly_numverts;
1674 mesh->numtriangles = surf->poly_numverts - 2;
1675 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1676 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1677 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1678 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1679 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1680 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1681 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1682 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1683 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1684 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1686 surf->lightmaptexturestride = 0;
1687 surf->lightmaptexture = NULL;
1689 for (i = 0;i < mesh->numverts;i++)
1691 mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1692 mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1693 mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1694 s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1695 t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1696 mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1697 mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1698 mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1699 mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1700 mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1701 mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1702 mesh->lightmapoffsets[i] = 0;
1705 for (i = 0;i < mesh->numtriangles;i++)
1707 mesh->element3i[i * 3 + 0] = 0;
1708 mesh->element3i[i * 3 + 1] = i + 1;
1709 mesh->element3i[i * 3 + 2] = i + 2;
1712 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1713 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1715 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1717 int i, iu, iv, smax, tmax;
1718 float u, v, ubase, vbase, uscale, vscale;
1720 smax = surf->extents[0] >> 4;
1721 tmax = surf->extents[1] >> 4;
1723 surf->flags |= SURF_LIGHTMAP;
1724 if (r_miplightmaps.integer)
1726 surf->lightmaptexturestride = smax+1;
1727 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1731 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1732 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1734 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1735 uscale = (uscale - ubase) / (smax + 1);
1736 vscale = (vscale - vbase) / (tmax + 1);
1738 for (i = 0;i < mesh->numverts;i++)
1740 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1741 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1742 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1743 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1744 // LordHavoc: calc lightmap data offset for vertex lighting to use
1747 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1753 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1755 node->parent = parent;
1756 if (node->contents < 0)
1758 Mod_Q1BSP_SetParent(node->children[0], node);
1759 Mod_Q1BSP_SetParent(node->children[1], node);
1762 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1768 in = (void *)(mod_base + l->fileofs);
1769 if (l->filelen % sizeof(*in))
1770 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1771 count = l->filelen / sizeof(*in);
1772 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1774 loadmodel->brushq1.nodes = out;
1775 loadmodel->brushq1.numnodes = count;
1777 for ( i=0 ; i<count ; i++, in++, out++)
1779 for (j=0 ; j<3 ; j++)
1781 out->mins[j] = LittleShort(in->mins[j]);
1782 out->maxs[j] = LittleShort(in->maxs[j]);
1785 p = LittleLong(in->planenum);
1786 out->plane = loadmodel->brushq1.planes + p;
1788 out->firstsurface = LittleShort(in->firstface);
1789 out->numsurfaces = LittleShort(in->numfaces);
1791 for (j=0 ; j<2 ; j++)
1793 p = LittleShort(in->children[j]);
1795 out->children[j] = loadmodel->brushq1.nodes + p;
1797 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1801 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1804 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1808 int i, j, count, p, pvschainbytes;
1811 in = (void *)(mod_base + l->fileofs);
1812 if (l->filelen % sizeof(*in))
1813 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1814 count = l->filelen / sizeof(*in);
1815 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1817 loadmodel->brushq1.leafs = out;
1818 loadmodel->brushq1.numleafs = count;
1819 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1820 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1822 for ( i=0 ; i<count ; i++, in++, out++)
1824 for (j=0 ; j<3 ; j++)
1826 out->mins[j] = LittleShort(in->mins[j]);
1827 out->maxs[j] = LittleShort(in->maxs[j]);
1830 // FIXME: this function could really benefit from some error checking
1832 out->contents = LittleLong(in->contents);
1834 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1835 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1836 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
1838 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);
1839 out->firstmarksurface = NULL;
1840 out->nummarksurfaces = 0;
1844 memset(out->pvsdata, 0xFF, pvschainbytes);
1845 pvs += pvschainbytes;
1847 p = LittleLong(in->visofs);
1850 if (p >= loadmodel->brushq1.num_compressedpvs)
1851 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
1853 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1856 for (j = 0;j < 4;j++)
1857 out->ambient_sound_level[j] = in->ambient_level[j];
1859 // FIXME: Insert caustics here
1863 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1865 dclipnode_t *in, *out;
1869 in = (void *)(mod_base + l->fileofs);
1870 if (l->filelen % sizeof(*in))
1871 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1872 count = l->filelen / sizeof(*in);
1873 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1875 loadmodel->brushq1.clipnodes = out;
1876 loadmodel->brushq1.numclipnodes = count;
1878 if (loadmodel->brush.ishlbsp)
1880 hull = &loadmodel->brushq1.hulls[1];
1881 hull->clipnodes = out;
1882 hull->firstclipnode = 0;
1883 hull->lastclipnode = count-1;
1884 hull->planes = loadmodel->brushq1.planes;
1885 hull->clip_mins[0] = -16;
1886 hull->clip_mins[1] = -16;
1887 hull->clip_mins[2] = -36;
1888 hull->clip_maxs[0] = 16;
1889 hull->clip_maxs[1] = 16;
1890 hull->clip_maxs[2] = 36;
1891 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1893 hull = &loadmodel->brushq1.hulls[2];
1894 hull->clipnodes = out;
1895 hull->firstclipnode = 0;
1896 hull->lastclipnode = count-1;
1897 hull->planes = loadmodel->brushq1.planes;
1898 hull->clip_mins[0] = -32;
1899 hull->clip_mins[1] = -32;
1900 hull->clip_mins[2] = -32;
1901 hull->clip_maxs[0] = 32;
1902 hull->clip_maxs[1] = 32;
1903 hull->clip_maxs[2] = 32;
1904 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1906 hull = &loadmodel->brushq1.hulls[3];
1907 hull->clipnodes = out;
1908 hull->firstclipnode = 0;
1909 hull->lastclipnode = count-1;
1910 hull->planes = loadmodel->brushq1.planes;
1911 hull->clip_mins[0] = -16;
1912 hull->clip_mins[1] = -16;
1913 hull->clip_mins[2] = -18;
1914 hull->clip_maxs[0] = 16;
1915 hull->clip_maxs[1] = 16;
1916 hull->clip_maxs[2] = 18;
1917 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1921 hull = &loadmodel->brushq1.hulls[1];
1922 hull->clipnodes = out;
1923 hull->firstclipnode = 0;
1924 hull->lastclipnode = count-1;
1925 hull->planes = loadmodel->brushq1.planes;
1926 hull->clip_mins[0] = -16;
1927 hull->clip_mins[1] = -16;
1928 hull->clip_mins[2] = -24;
1929 hull->clip_maxs[0] = 16;
1930 hull->clip_maxs[1] = 16;
1931 hull->clip_maxs[2] = 32;
1932 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1934 hull = &loadmodel->brushq1.hulls[2];
1935 hull->clipnodes = out;
1936 hull->firstclipnode = 0;
1937 hull->lastclipnode = count-1;
1938 hull->planes = loadmodel->brushq1.planes;
1939 hull->clip_mins[0] = -32;
1940 hull->clip_mins[1] = -32;
1941 hull->clip_mins[2] = -24;
1942 hull->clip_maxs[0] = 32;
1943 hull->clip_maxs[1] = 32;
1944 hull->clip_maxs[2] = 64;
1945 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1948 for (i=0 ; i<count ; i++, out++, in++)
1950 out->planenum = LittleLong(in->planenum);
1951 out->children[0] = LittleShort(in->children[0]);
1952 out->children[1] = LittleShort(in->children[1]);
1953 if (out->children[0] >= count || out->children[1] >= count)
1954 Host_Error("Corrupt clipping hull(out of range child)\n");
1958 //Duplicate the drawing hull structure as a clipping hull
1959 static void Mod_Q1BSP_MakeHull0(void)
1966 hull = &loadmodel->brushq1.hulls[0];
1968 in = loadmodel->brushq1.nodes;
1969 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1971 hull->clipnodes = out;
1972 hull->firstclipnode = 0;
1973 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1974 hull->planes = loadmodel->brushq1.planes;
1976 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1978 out->planenum = in->plane - loadmodel->brushq1.planes;
1979 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1980 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1984 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1989 in = (void *)(mod_base + l->fileofs);
1990 if (l->filelen % sizeof(*in))
1991 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1992 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1993 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1995 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1997 j = (unsigned) LittleShort(in[i]);
1998 if (j >= loadmodel->brushq1.numsurfaces)
1999 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2000 loadmodel->brushq1.marksurfaces[i] = j;
2004 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2009 in = (void *)(mod_base + l->fileofs);
2010 if (l->filelen % sizeof(*in))
2011 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2012 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2013 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2015 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2016 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2020 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2026 in = (void *)(mod_base + l->fileofs);
2027 if (l->filelen % sizeof(*in))
2028 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2030 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2031 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2033 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2035 out->normal[0] = LittleFloat(in->normal[0]);
2036 out->normal[1] = LittleFloat(in->normal[1]);
2037 out->normal[2] = LittleFloat(in->normal[2]);
2038 out->dist = LittleFloat(in->dist);
2044 typedef struct portal_s
2047 mnode_t *nodes[2]; // [0] = front side of plane
2048 struct portal_s *next[2];
2050 struct portal_s *chain; // all portals are linked into a list
2054 static portal_t *portalchain;
2061 static portal_t *AllocPortal(void)
2064 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2065 p->chain = portalchain;
2070 static void FreePortal(portal_t *p)
2075 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2077 // calculate children first
2078 if (node->children[0]->contents >= 0)
2079 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2080 if (node->children[1]->contents >= 0)
2081 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2083 // make combined bounding box from children
2084 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2085 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2086 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2087 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2088 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2089 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2092 static void Mod_Q1BSP_FinalizePortals(void)
2094 int i, j, numportals, numpoints;
2095 portal_t *p, *pnext;
2098 mleaf_t *leaf, *endleaf;
2101 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2102 leaf = loadmodel->brushq1.leafs;
2103 endleaf = leaf + loadmodel->brushq1.numleafs;
2104 for (;leaf < endleaf;leaf++)
2106 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2107 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2114 for (i = 0;i < 2;i++)
2116 leaf = (mleaf_t *)p->nodes[i];
2118 for (j = 0;j < w->numpoints;j++)
2120 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2121 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2122 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2123 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2124 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2125 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2132 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2134 // tally up portal and point counts
2140 // note: this check must match the one below or it will usually corrupt memory
2141 // 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
2142 if (p->winding && p->nodes[0] != p->nodes[1]
2143 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2144 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2147 numpoints += p->winding->numpoints * 2;
2151 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2152 loadmodel->brushq1.numportals = numportals;
2153 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2154 loadmodel->brushq1.numportalpoints = numpoints;
2155 // clear all leaf portal chains
2156 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2157 loadmodel->brushq1.leafs[i].portals = NULL;
2158 // process all portals in the global portal chain, while freeing them
2159 portal = loadmodel->brushq1.portals;
2160 point = loadmodel->brushq1.portalpoints;
2169 // note: this check must match the one above or it will usually corrupt memory
2170 // 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
2171 if (p->nodes[0] != p->nodes[1]
2172 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2173 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2175 // first make the back to front portal(forward portal)
2176 portal->points = point;
2177 portal->numpoints = p->winding->numpoints;
2178 portal->plane.dist = p->plane.dist;
2179 VectorCopy(p->plane.normal, portal->plane.normal);
2180 portal->here = (mleaf_t *)p->nodes[1];
2181 portal->past = (mleaf_t *)p->nodes[0];
2183 for (j = 0;j < portal->numpoints;j++)
2185 VectorCopy(p->winding->points[j], point->position);
2188 PlaneClassify(&portal->plane);
2190 // link into leaf's portal chain
2191 portal->next = portal->here->portals;
2192 portal->here->portals = portal;
2194 // advance to next portal
2197 // then make the front to back portal(backward portal)
2198 portal->points = point;
2199 portal->numpoints = p->winding->numpoints;
2200 portal->plane.dist = -p->plane.dist;
2201 VectorNegate(p->plane.normal, portal->plane.normal);
2202 portal->here = (mleaf_t *)p->nodes[0];
2203 portal->past = (mleaf_t *)p->nodes[1];
2205 for (j = portal->numpoints - 1;j >= 0;j--)
2207 VectorCopy(p->winding->points[j], point->position);
2210 PlaneClassify(&portal->plane);
2212 // link into leaf's portal chain
2213 portal->next = portal->here->portals;
2214 portal->here->portals = portal;
2216 // advance to next portal
2219 Winding_Free(p->winding);
2231 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2234 Host_Error("AddPortalToNodes: NULL front node");
2236 Host_Error("AddPortalToNodes: NULL back node");
2237 if (p->nodes[0] || p->nodes[1])
2238 Host_Error("AddPortalToNodes: already included");
2239 // 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
2241 p->nodes[0] = front;
2242 p->next[0] = (portal_t *)front->portals;
2243 front->portals = (mportal_t *)p;
2246 p->next[1] = (portal_t *)back->portals;
2247 back->portals = (mportal_t *)p;
2252 RemovePortalFromNode
2255 static void RemovePortalFromNodes(portal_t *portal)
2259 void **portalpointer;
2261 for (i = 0;i < 2;i++)
2263 node = portal->nodes[i];
2265 portalpointer = (void **) &node->portals;
2270 Host_Error("RemovePortalFromNodes: portal not in leaf");
2274 if (portal->nodes[0] == node)
2276 *portalpointer = portal->next[0];
2277 portal->nodes[0] = NULL;
2279 else if (portal->nodes[1] == node)
2281 *portalpointer = portal->next[1];
2282 portal->nodes[1] = NULL;
2285 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2289 if (t->nodes[0] == node)
2290 portalpointer = (void **) &t->next[0];
2291 else if (t->nodes[1] == node)
2292 portalpointer = (void **) &t->next[1];
2294 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2299 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2302 mnode_t *front, *back, *other_node;
2303 mplane_t clipplane, *plane;
2304 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2305 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2307 // if a leaf, we're done
2311 plane = node->plane;
2313 front = node->children[0];
2314 back = node->children[1];
2316 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2318 // create the new portal by generating a polygon for the node plane,
2319 // and clipping it by all of the other portals(which came from nodes above this one)
2320 nodeportal = AllocPortal();
2321 nodeportal->plane = *plane;
2323 nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2324 side = 0; // shut up compiler warning
2325 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2327 clipplane = portal->plane;
2328 if (portal->nodes[0] == portal->nodes[1])
2329 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2330 if (portal->nodes[0] == node)
2332 else if (portal->nodes[1] == node)
2334 clipplane.dist = -clipplane.dist;
2335 VectorNegate(clipplane.normal, clipplane.normal);
2339 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2341 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2342 if (!nodeportalwinding)
2344 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2349 if (nodeportalwinding)
2351 // if the plane was not clipped on all sides, there was an error
2352 nodeportal->winding = nodeportalwinding;
2353 AddPortalToNodes(nodeportal, front, back);
2356 // split the portals of this node along this node's plane and assign them to the children of this node
2357 // (migrating the portals downward through the tree)
2358 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2360 if (portal->nodes[0] == portal->nodes[1])
2361 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2362 if (portal->nodes[0] == node)
2364 else if (portal->nodes[1] == node)
2367 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2368 nextportal = portal->next[side];
2370 other_node = portal->nodes[!side];
2371 RemovePortalFromNodes(portal);
2373 // cut the portal into two portals, one on each side of the node plane
2374 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2379 AddPortalToNodes(portal, back, other_node);
2381 AddPortalToNodes(portal, other_node, back);
2387 AddPortalToNodes(portal, front, other_node);
2389 AddPortalToNodes(portal, other_node, front);
2393 // the winding is split
2394 splitportal = AllocPortal();
2395 temp = splitportal->chain;
2396 *splitportal = *portal;
2397 splitportal->chain = temp;
2398 splitportal->winding = backwinding;
2399 Winding_Free(portal->winding);
2400 portal->winding = frontwinding;
2404 AddPortalToNodes(portal, front, other_node);
2405 AddPortalToNodes(splitportal, back, other_node);
2409 AddPortalToNodes(portal, other_node, front);
2410 AddPortalToNodes(splitportal, other_node, back);
2414 Mod_Q1BSP_RecursiveNodePortals(front);
2415 Mod_Q1BSP_RecursiveNodePortals(back);
2418 static void Mod_Q1BSP_MakePortals(void)
2421 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2422 Mod_Q1BSP_FinalizePortals();
2425 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2428 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2429 msurface_t *surf, *s;
2430 float *v0, *v1, *v2, *v3;
2431 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2432 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2433 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2435 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)
2437 if (surf->neighborsurfaces[vertnum])
2439 surf->neighborsurfaces[vertnum] = NULL;
2440 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2442 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2443 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2444 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2447 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2448 if (s->neighborsurfaces[vnum] == surf)
2450 if (vnum < s->poly_numverts)
2452 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)
2454 if (s->neighborsurfaces[vnum] == NULL
2455 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2456 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2458 surf->neighborsurfaces[vertnum] = s;
2459 s->neighborsurfaces[vnum] = surf;
2463 if (vnum < s->poly_numverts)
2471 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2473 int i, j, stylecounts[256], totalcount, remapstyles[256];
2475 memset(stylecounts, 0, sizeof(stylecounts));
2476 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2478 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2479 for (j = 0;j < MAXLIGHTMAPS;j++)
2480 stylecounts[surf->styles[j]]++;
2483 model->brushq1.light_styles = 0;
2484 for (i = 0;i < 255;i++)
2488 remapstyles[i] = model->brushq1.light_styles++;
2489 totalcount += stylecounts[i] + 1;
2494 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2495 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2496 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2497 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2498 model->brushq1.light_styles = 0;
2499 for (i = 0;i < 255;i++)
2501 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2503 for (i = 0;i < model->brushq1.light_styles;i++)
2505 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2506 j += stylecounts[model->brushq1.light_style[i]] + 1;
2508 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2510 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2511 for (j = 0;j < MAXLIGHTMAPS;j++)
2512 if (surf->styles[j] != 255)
2513 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2516 for (i = 0;i < model->brushq1.light_styles;i++)
2518 *model->brushq1.light_styleupdatechains[i] = NULL;
2519 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2520 j += stylecounts[model->brushq1.light_style[i]] + 1;
2524 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2527 for (i = 0;i < model->brushq1.numtextures;i++)
2528 model->brushq1.pvstexturechainslength[i] = 0;
2529 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2531 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2533 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2534 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2537 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2539 if (model->brushq1.pvstexturechainslength[i])
2541 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2542 j += model->brushq1.pvstexturechainslength[i] + 1;
2545 model->brushq1.pvstexturechains[i] = NULL;
2547 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2548 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2549 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2550 for (i = 0;i < model->brushq1.numtextures;i++)
2552 if (model->brushq1.pvstexturechainslength[i])
2554 *model->brushq1.pvstexturechains[i] = NULL;
2555 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2560 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2565 while (node->contents >= 0)
2567 d = PlaneDiff(org, node->plane);
2569 node = node->children[0];
2570 else if (d < -radius)
2571 node = node->children[1];
2574 // go down both sides
2575 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2576 node = node->children[1];
2580 // if this is a leaf, accumulate the pvs bits
2581 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2582 for (i = 0;i < pvsbytes;i++)
2583 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2586 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2587 //of the given point.
2588 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2590 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2591 bytes = min(bytes, pvsbufferlength);
2592 memset(pvsbuffer, 0, bytes);
2593 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2597 //Returns PVS data for a given point
2598 //(note: always returns valid data, never NULL)
2599 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2602 Mod_CheckLoaded(model);
2603 // LordHavoc: modified to start at first clip node,
2604 // in other words: first node of the (sub)model
2605 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2606 while (node->contents == 0)
2607 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2608 return ((mleaf_t *)node)->pvsdata;
2611 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2616 VectorSubtract(inmaxs, inmins, size);
2617 if (cmodel->brush.ishlbsp)
2620 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2621 else if (size[0] <= 32)
2623 if (size[2] < 54) // pick the nearest of 36 or 72
2624 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2626 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2629 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2634 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2635 else if (size[0] <= 32)
2636 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2638 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2640 VectorCopy(inmins, outmins);
2641 VectorAdd(inmins, hull->clip_size, outmaxs);
2644 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2645 extern void R_Model_Brush_Draw(entity_render_t *ent);
2646 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2647 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);
2648 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2653 mempool_t *mainmempool;
2655 model_t *originalloadmodel;
2656 float dist, modelyawradius, modelradius, *vec;
2660 mod->type = mod_brush;
2662 header = (dheader_t *)buffer;
2664 i = LittleLong(header->version);
2665 if (i != BSPVERSION && i != 30)
2666 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2667 mod->brush.ishlbsp = i == 30;
2669 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2670 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2671 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2672 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2673 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2674 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2675 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2676 mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
2677 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2678 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2679 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2680 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2682 if (loadmodel->isworldmodel)
2684 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2685 // until we get a texture for it...
2689 // swap all the lumps
2690 mod_base = (qbyte *)header;
2692 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2693 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2697 // store which lightmap format to use
2698 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2700 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2701 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2702 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2703 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2704 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2705 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2706 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2707 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2708 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2709 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2710 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2711 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2712 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2713 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2714 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2716 if (mod->brushq1.data_compressedpvs)
2717 Mem_Free(mod->brushq1.data_compressedpvs);
2718 mod->brushq1.data_compressedpvs = NULL;
2719 mod->brushq1.num_compressedpvs = 0;
2721 Mod_Q1BSP_MakeHull0();
2722 Mod_Q1BSP_MakePortals();
2724 if (developer.integer)
2725 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);
2727 mod->numframes = 2; // regular and alternate animation
2729 mainmempool = mod->mempool;
2730 loadname = mod->name;
2732 Mod_Q1BSP_LoadLightList();
2733 originalloadmodel = loadmodel;
2736 // set up the submodels(FIXME: this is confusing)
2738 for (i = 0;i < mod->brush.numsubmodels;i++)
2740 bm = &mod->brushq1.submodels[i];
2742 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2743 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2745 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2746 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2749 mod->brushq1.firstmodelsurface = bm->firstface;
2750 mod->brushq1.nummodelsurfaces = bm->numfaces;
2752 // this gets altered below if sky is used
2753 mod->DrawSky = NULL;
2754 mod->Draw = R_Model_Brush_Draw;
2755 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2756 mod->DrawLight = R_Model_Brush_DrawLight;
2757 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2758 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2759 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2760 Mod_Q1BSP_BuildPVSTextureChains(mod);
2761 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2762 if (mod->brushq1.nummodelsurfaces)
2764 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2765 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2766 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2769 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2771 // we only need to have a drawsky function if it is used(usually only on world model)
2772 if (surf->texinfo->texture->shader == &Cshader_sky)
2773 mod->DrawSky = R_Model_Brush_DrawSky;
2774 // LordHavoc: submodels always clip, even if water
2775 if (mod->brush.numsubmodels - 1)
2776 surf->flags |= SURF_SOLIDCLIP;
2777 // calculate bounding shapes
2778 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2780 for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2782 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2783 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2784 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2785 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2786 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2787 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2788 dist = vec[0]*vec[0]+vec[1]*vec[1];
2789 if (modelyawradius < dist)
2790 modelyawradius = dist;
2791 dist += vec[2]*vec[2];
2792 if (modelradius < dist)
2797 modelyawradius = sqrt(modelyawradius);
2798 modelradius = sqrt(modelradius);
2799 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2800 mod->yawmins[2] = mod->normalmins[2];
2801 mod->yawmaxs[2] = mod->normalmaxs[2];
2802 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2803 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2804 mod->radius = modelradius;
2805 mod->radius2 = modelradius * modelradius;
2809 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2810 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2812 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2814 mod->brushq1.visleafs = bm->visleafs;
2816 // LordHavoc: only register submodels if it is the world
2817 // (prevents bsp models from replacing world submodels)
2818 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2821 // duplicate the basic information
2822 sprintf(name, "*%i", i+1);
2823 loadmodel = Mod_FindName(name);
2825 strcpy(loadmodel->name, name);
2826 // textures and memory belong to the main model
2827 loadmodel->texturepool = NULL;
2828 loadmodel->mempool = NULL;
2833 loadmodel = originalloadmodel;
2834 //Mod_Q1BSP_ProcessLightList();
2837 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2841 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2848 in = (void *)(mod_base + l->fileofs);
2849 if (l->filelen % sizeof(*in))
2850 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2851 count = l->filelen / sizeof(*in);
2852 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2855 loadmodel->num = count;
2857 for (i = 0;i < count;i++, in++, out++)
2863 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2870 in = (void *)(mod_base + l->fileofs);
2871 if (l->filelen % sizeof(*in))
2872 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2873 count = l->filelen / sizeof(*in);
2874 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2877 loadmodel->num = count;
2879 for (i = 0;i < count;i++, in++, out++)
2885 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2892 in = (void *)(mod_base + l->fileofs);
2893 if (l->filelen % sizeof(*in))
2894 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2895 count = l->filelen / sizeof(*in);
2896 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2899 loadmodel->num = count;
2901 for (i = 0;i < count;i++, in++, out++)
2907 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2914 in = (void *)(mod_base + l->fileofs);
2915 if (l->filelen % sizeof(*in))
2916 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2917 count = l->filelen / sizeof(*in);
2918 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2921 loadmodel->num = count;
2923 for (i = 0;i < count;i++, in++, out++)
2929 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2936 in = (void *)(mod_base + l->fileofs);
2937 if (l->filelen % sizeof(*in))
2938 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2939 count = l->filelen / sizeof(*in);
2940 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2943 loadmodel->num = count;
2945 for (i = 0;i < count;i++, in++, out++)
2951 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2958 in = (void *)(mod_base + l->fileofs);
2959 if (l->filelen % sizeof(*in))
2960 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2961 count = l->filelen / sizeof(*in);
2962 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2965 loadmodel->num = count;
2967 for (i = 0;i < count;i++, in++, out++)
2973 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2980 in = (void *)(mod_base + l->fileofs);
2981 if (l->filelen % sizeof(*in))
2982 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2983 count = l->filelen / sizeof(*in);
2984 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2987 loadmodel->num = count;
2989 for (i = 0;i < count;i++, in++, out++)
2995 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3002 in = (void *)(mod_base + l->fileofs);
3003 if (l->filelen % sizeof(*in))
3004 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3005 count = l->filelen / sizeof(*in);
3006 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3009 loadmodel->num = count;
3011 for (i = 0;i < count;i++, in++, out++)
3017 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3024 in = (void *)(mod_base + l->fileofs);
3025 if (l->filelen % sizeof(*in))
3026 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3027 count = l->filelen / sizeof(*in);
3028 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3031 loadmodel->num = count;
3033 for (i = 0;i < count;i++, in++, out++)
3039 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3046 in = (void *)(mod_base + l->fileofs);
3047 if (l->filelen % sizeof(*in))
3048 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3049 count = l->filelen / sizeof(*in);
3050 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3053 loadmodel->num = count;
3055 for (i = 0;i < count;i++, in++, out++)
3061 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3068 in = (void *)(mod_base + l->fileofs);
3069 if (l->filelen % sizeof(*in))
3070 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3071 count = l->filelen / sizeof(*in);
3072 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3075 loadmodel->num = count;
3077 for (i = 0;i < count;i++, in++, out++)
3083 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3090 in = (void *)(mod_base + l->fileofs);
3091 if (l->filelen % sizeof(*in))
3092 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3093 count = l->filelen / sizeof(*in);
3094 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3097 loadmodel->num = count;
3099 for (i = 0;i < count;i++, in++, out++)
3105 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3112 in = (void *)(mod_base + l->fileofs);
3113 if (l->filelen % sizeof(*in))
3114 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3115 count = l->filelen / sizeof(*in);
3116 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3119 loadmodel->num = count;
3121 for (i = 0;i < count;i++, in++, out++)
3127 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3134 in = (void *)(mod_base + l->fileofs);
3135 if (l->filelen % sizeof(*in))
3136 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3137 count = l->filelen / sizeof(*in);
3138 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3141 loadmodel->num = count;
3143 for (i = 0;i < count;i++, in++, out++)
3149 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3156 in = (void *)(mod_base + l->fileofs);
3157 if (l->filelen % sizeof(*in))
3158 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3159 count = l->filelen / sizeof(*in);
3160 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3163 loadmodel->num = count;
3165 for (i = 0;i < count;i++, in++, out++)
3171 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3178 in = (void *)(mod_base + l->fileofs);
3179 if (l->filelen % sizeof(*in))
3180 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3181 count = l->filelen / sizeof(*in);
3182 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3185 loadmodel->num = count;
3187 for (i = 0;i < count;i++, in++, out++)
3193 static void Mod_Q2BSP_LoadModels(lump_t *l)
3200 in = (void *)(mod_base + l->fileofs);
3201 if (l->filelen % sizeof(*in))
3202 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3203 count = l->filelen / sizeof(*in);
3204 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3207 loadmodel->num = count;
3209 for (i = 0;i < count;i++, in++, out++)
3215 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3218 q2dheader_t *header;
3220 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3222 mod->type = mod_brushq2;
3224 header = (q2dheader_t *)buffer;
3226 i = LittleLong(header->version);
3227 if (i != Q2BSPVERSION)
3228 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3229 mod->brush.ishlbsp = false;
3230 if (loadmodel->isworldmodel)
3232 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3233 // until we get a texture for it...
3237 mod_base = (qbyte *)header;
3239 // swap all the lumps
3240 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3241 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3243 // store which lightmap format to use
3244 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3246 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3247 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3248 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3249 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3250 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3251 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3252 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3253 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3254 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3255 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3256 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3257 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3258 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3259 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3260 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3261 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3262 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3263 // LordHavoc: must go last because this makes the submodels
3264 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3267 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3268 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3270 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3273 char key[128], value[4096];
3275 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3276 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3277 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3280 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3281 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3282 data = loadmodel->brush.entities;
3283 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3284 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3288 if (!COM_ParseToken(&data, false))
3290 if (com_token[0] == '}')
3291 break; // end of worldspawn
3292 if (com_token[0] == '_')
3293 strcpy(key, com_token + 1);
3295 strcpy(key, com_token);
3296 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3297 key[strlen(key)-1] = 0;
3298 if (!COM_ParseToken(&data, false))
3300 strcpy(value, com_token);
3301 if (!strcmp("gridsize", key))
3303 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3304 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3310 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3316 in = (void *)(mod_base + l->fileofs);
3317 if (l->filelen % sizeof(*in))
3318 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3319 count = l->filelen / sizeof(*in);
3320 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3322 loadmodel->brushq3.data_textures = out;
3323 loadmodel->brushq3.num_textures = count;
3325 for (i = 0;i < count;i++, in++, out++)
3327 strncpy(out->name, in->name, sizeof(out->name) - 1);
3328 out->surfaceflags = LittleLong(in->surfaceflags);
3329 out->nativecontents = LittleLong(in->contents);
3330 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3331 out->renderflags = 0;
3332 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3333 out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3334 if (!strncmp(out->name, "textures/skies/", 15))
3335 out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY;
3338 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3342 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3348 in = (void *)(mod_base + l->fileofs);
3349 if (l->filelen % sizeof(*in))
3350 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3351 count = l->filelen / sizeof(*in);
3352 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3354 loadmodel->brushq3.data_planes = out;
3355 loadmodel->brushq3.num_planes = count;
3357 for (i = 0;i < count;i++, in++, out++)
3359 out->normal[0] = LittleLong(in->normal[0]);
3360 out->normal[1] = LittleLong(in->normal[1]);
3361 out->normal[2] = LittleLong(in->normal[2]);
3362 out->dist = LittleLong(in->dist);
3367 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3370 q3mbrushside_t *out;
3373 in = (void *)(mod_base + l->fileofs);
3374 if (l->filelen % sizeof(*in))
3375 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3376 count = l->filelen / sizeof(*in);
3377 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3379 loadmodel->brushq3.data_brushsides = out;
3380 loadmodel->brushq3.num_brushsides = count;
3382 for (i = 0;i < count;i++, in++, out++)
3384 n = LittleLong(in->planeindex);
3385 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3386 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3387 out->plane = loadmodel->brushq3.data_planes + n;
3388 n = LittleLong(in->textureindex);
3389 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3390 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3391 out->texture = loadmodel->brushq3.data_textures + n;
3395 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3399 int i, j, n, c, count, maxplanes;
3402 in = (void *)(mod_base + l->fileofs);
3403 if (l->filelen % sizeof(*in))
3404 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3405 count = l->filelen / sizeof(*in);
3406 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3408 loadmodel->brushq3.data_brushes = out;
3409 loadmodel->brushq3.num_brushes = count;
3414 for (i = 0;i < count;i++, in++, out++)
3416 n = LittleLong(in->firstbrushside);
3417 c = LittleLong(in->numbrushsides);
3418 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3419 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3420 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3421 out->numbrushsides = c;
3422 n = LittleLong(in->textureindex);
3423 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3424 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3425 out->texture = loadmodel->brushq3.data_textures + n;
3427 // make a list of mplane_t structs to construct a colbrush from
3428 if (maxplanes < out->numbrushsides)
3430 maxplanes = out->numbrushsides;
3433 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3435 for (j = 0;j < out->numbrushsides;j++)
3437 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3438 planes[j].dist = out->firstbrushside[j].plane->dist;
3440 // make the colbrush from the planes
3441 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3447 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3453 in = (void *)(mod_base + l->fileofs);
3454 if (l->filelen % sizeof(*in))
3455 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3456 count = l->filelen / sizeof(*in);
3457 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3459 loadmodel->brushq3.data_effects = out;
3460 loadmodel->brushq3.num_effects = count;
3462 for (i = 0;i < count;i++, in++, out++)
3464 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3465 n = LittleLong(in->brushindex);
3466 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3467 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3468 out->brush = loadmodel->brushq3.data_brushes + n;
3469 out->unknown = LittleLong(in->unknown);
3473 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3478 in = (void *)(mod_base + l->fileofs);
3479 if (l->filelen % sizeof(*in))
3480 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3481 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3482 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3483 loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3484 loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3485 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3486 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3487 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3488 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3490 for (i = 0;i < count;i++, in++)
3492 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3493 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3494 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3495 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3496 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3497 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3498 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3499 // svector/tvector are calculated later in face loading
3500 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3501 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3502 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3503 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3504 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3505 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3506 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3507 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3508 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3509 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3510 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3511 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3512 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3516 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3522 in = (void *)(mod_base + l->fileofs);
3523 if (l->filelen % sizeof(int[3]))
3524 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3525 count = l->filelen / sizeof(*in);
3526 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3528 loadmodel->brushq3.num_triangles = count / 3;
3529 loadmodel->brushq3.data_element3i = out;
3530 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3532 for (i = 0;i < count;i++, in++, out++)
3534 *out = LittleLong(*in);
3535 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3537 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3543 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3549 in = (void *)(mod_base + l->fileofs);
3550 if (l->filelen % sizeof(*in))
3551 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3552 count = l->filelen / sizeof(*in);
3553 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3555 loadmodel->brushq3.data_lightmaps = out;
3556 loadmodel->brushq3.num_lightmaps = count;
3558 for (i = 0;i < count;i++, in++, out++)
3559 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3562 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3566 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3567 //int *originalelement3i;
3568 //int *originalneighbor3i;
3569 float *originalvertex3f;
3570 //float *originalsvector3f;
3571 //float *originaltvector3f;
3572 //float *originalnormal3f;
3573 float *originalcolor4f;
3574 float *originaltexcoordtexture2f;
3575 float *originaltexcoordlightmap2f;
3578 in = (void *)(mod_base + l->fileofs);
3579 if (l->filelen % sizeof(*in))
3580 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3581 count = l->filelen / sizeof(*in);
3582 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3584 loadmodel->brushq3.data_faces = out;
3585 loadmodel->brushq3.num_faces = count;
3587 for (i = 0;i < count;i++, in++, out++)
3589 // check face type first
3590 out->type = LittleLong(in->type);
3591 if (out->type != Q3FACETYPE_POLYGON
3592 && out->type != Q3FACETYPE_PATCH
3593 && out->type != Q3FACETYPE_MESH
3594 && out->type != Q3FACETYPE_FLARE)
3596 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3597 out->type = 0; // error
3601 n = LittleLong(in->textureindex);
3602 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3604 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3605 out->type = 0; // error
3609 out->texture = loadmodel->brushq3.data_textures + n;
3610 n = LittleLong(in->effectindex);
3611 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3613 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3619 out->effect = loadmodel->brushq3.data_effects + n;
3620 n = LittleLong(in->lightmapindex);
3621 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3623 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3627 out->lightmaptexture = NULL;
3629 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3631 out->firstvertex = LittleLong(in->firstvertex);
3632 out->numvertices = LittleLong(in->numvertices);
3633 out->firstelement = LittleLong(in->firstelement);
3634 out->numelements = LittleLong(in->numelements);
3635 out->numtriangles = out->numelements / 3;
3636 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3638 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->numvertices, loadmodel->brushq3.num_vertices);
3639 out->type = 0; // error
3642 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3644 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->numelements, loadmodel->brushq3.num_triangles * 3);
3645 out->type = 0; // error
3648 if (out->numtriangles * 3 != out->numelements)
3650 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3651 out->type = 0; // error
3654 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3655 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3656 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3657 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3658 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3659 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3660 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3661 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3662 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3665 case Q3FACETYPE_POLYGON:
3666 case Q3FACETYPE_MESH:
3667 // no processing necessary
3669 case Q3FACETYPE_PATCH:
3670 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3671 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3672 if (patchsize[0] < 1 || patchsize[1] < 1)
3674 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3675 out->type = 0; // error
3678 // convert patch to Q3FACETYPE_MESH
3679 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3680 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3681 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3682 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3683 finalvertices = finalwidth * finalheight;
3684 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3685 originalvertex3f = out->data_vertex3f;
3686 //originalsvector3f = out->data_svector3f;
3687 //originaltvector3f = out->data_tvector3f;
3688 //originalnormal3f = out->data_normal3f;
3689 originalcolor4f = out->data_color4f;
3690 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3691 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3692 //originalelement3i = out->data_element3i;
3693 //originalneighbor3i = out->data_neighbor3i;
3694 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
3695 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3696 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3697 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3698 out->data_color4f = out->data_normal3f + finalvertices * 3;
3699 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3700 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3701 out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
3702 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3703 out->type = Q3FACETYPE_MESH;
3704 out->firstvertex = -1;
3705 out->numvertices = finalvertices;
3706 out->firstelement = -1;
3707 out->numtriangles = finaltriangles;
3708 out->numelements = finaltriangles * 3;
3709 // generate geometry
3710 // (note: normals are skipped because they get recalculated)
3711 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3712 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3713 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3714 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3715 // generate elements
3716 e = out->data_element3i;
3717 for (y = 0;y < finalheight - 1;y++)
3719 row0 = (y + 0) * finalwidth;
3720 row1 = (y + 1) * finalwidth;
3721 for (x = 0;x < finalwidth - 1;x++)
3733 out->numtriangles = Mod_RemoveDegenerateTriangles(out->numtriangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3734 if (developer.integer)
3736 if (out->numtriangles < finaltriangles)
3737 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->numvertices, finaltriangles, finaltriangles - out->numtriangles, out->numtriangles);
3739 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->numvertices, out->numtriangles);
3741 // q3map does not put in collision brushes for curves... ugh
3742 out->collisions = true;
3744 case Q3FACETYPE_FLARE:
3745 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3747 out->numtriangles = 0;
3750 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3751 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3753 if (invalidelements)
3755 Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->numvertices, out->firstelement, out->numelements);
3756 for (j = 0;j < out->numelements;j++)
3758 Con_Printf(" %i", out->data_element3i[j]);
3759 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3760 out->data_element3i[j] = 0;
3764 // for shadow volumes
3765 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->numtriangles);
3766 // for per pixel lighting
3767 Mod_BuildTextureVectorsAndNormals(out->numvertices, out->numtriangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
3768 // calculate a bounding box
3769 VectorClear(out->mins);
3770 VectorClear(out->maxs);
3771 if (out->numvertices)
3773 VectorCopy(out->data_vertex3f, out->mins);
3774 VectorCopy(out->data_vertex3f, out->maxs);
3775 for (j = 1, v = out->data_vertex3f + 3;j < out->numvertices;j++, v += 3)
3777 out->mins[0] = min(out->mins[0], v[0]);
3778 out->maxs[0] = max(out->maxs[0], v[0]);
3779 out->mins[1] = min(out->mins[1], v[1]);
3780 out->maxs[1] = max(out->maxs[1], v[1]);
3781 out->mins[2] = min(out->mins[2], v[2]);
3782 out->maxs[2] = max(out->maxs[2], v[2]);
3784 out->mins[0] -= 1.0f;
3785 out->mins[1] -= 1.0f;
3786 out->mins[2] -= 1.0f;
3787 out->maxs[0] += 1.0f;
3788 out->maxs[1] += 1.0f;
3789 out->maxs[2] += 1.0f;
3794 static void Mod_Q3BSP_LoadModels(lump_t *l)
3798 int i, j, n, c, count;
3800 in = (void *)(mod_base + l->fileofs);
3801 if (l->filelen % sizeof(*in))
3802 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3803 count = l->filelen / sizeof(*in);
3804 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3806 loadmodel->brushq3.data_models = out;
3807 loadmodel->brushq3.num_models = count;
3809 for (i = 0;i < count;i++, in++, out++)
3811 for (j = 0;j < 3;j++)
3813 out->mins[j] = LittleFloat(in->mins[j]);
3814 out->maxs[j] = LittleFloat(in->maxs[j]);
3816 n = LittleLong(in->firstface);
3817 c = LittleLong(in->numfaces);
3818 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3819 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3820 out->firstface = loadmodel->brushq3.data_faces + n;
3822 n = LittleLong(in->firstbrush);
3823 c = LittleLong(in->numbrushes);
3824 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3825 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3826 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3827 out->numbrushes = c;
3831 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3837 in = (void *)(mod_base + l->fileofs);
3838 if (l->filelen % sizeof(*in))
3839 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3840 count = l->filelen / sizeof(*in);
3841 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3843 loadmodel->brushq3.data_leafbrushes = out;
3844 loadmodel->brushq3.num_leafbrushes = count;
3846 for (i = 0;i < count;i++, in++, out++)
3848 n = LittleLong(*in);
3849 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3850 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3851 *out = loadmodel->brushq3.data_brushes + n;
3855 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3861 in = (void *)(mod_base + l->fileofs);
3862 if (l->filelen % sizeof(*in))
3863 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3864 count = l->filelen / sizeof(*in);
3865 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3867 loadmodel->brushq3.data_leaffaces = out;
3868 loadmodel->brushq3.num_leaffaces = count;
3870 for (i = 0;i < count;i++, in++, out++)
3872 n = LittleLong(*in);
3873 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3874 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3875 *out = loadmodel->brushq3.data_faces + n;
3879 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3883 int i, j, n, c, count;
3885 in = (void *)(mod_base + l->fileofs);
3886 if (l->filelen % sizeof(*in))
3887 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3888 count = l->filelen / sizeof(*in);
3889 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3891 loadmodel->brushq3.data_leafs = out;
3892 loadmodel->brushq3.num_leafs = count;
3894 for (i = 0;i < count;i++, in++, out++)
3896 out->isnode = false;
3898 out->clusterindex = LittleLong(in->clusterindex);
3899 out->areaindex = LittleLong(in->areaindex);
3900 for (j = 0;j < 3;j++)
3902 // yes the mins/maxs are ints
3903 out->mins[j] = LittleLong(in->mins[j]);
3904 out->maxs[j] = LittleLong(in->maxs[j]);
3906 n = LittleLong(in->firstleafface);
3907 c = LittleLong(in->numleaffaces);
3908 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3909 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3910 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3911 out->numleaffaces = c;
3912 n = LittleLong(in->firstleafbrush);
3913 c = LittleLong(in->numleafbrushes);
3914 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3915 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3916 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3917 out->numleafbrushes = c;
3921 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3924 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3925 node->parent = parent;
3928 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3929 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3933 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3939 in = (void *)(mod_base + l->fileofs);
3940 if (l->filelen % sizeof(*in))
3941 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3942 count = l->filelen / sizeof(*in);
3943 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3945 loadmodel->brushq3.data_nodes = out;
3946 loadmodel->brushq3.num_nodes = count;
3948 for (i = 0;i < count;i++, in++, out++)
3952 n = LittleLong(in->planeindex);
3953 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3954 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3955 out->plane = loadmodel->brushq3.data_planes + n;
3956 for (j = 0;j < 2;j++)
3958 n = LittleLong(in->childrenindex[j]);
3961 if (n >= loadmodel->brushq3.num_nodes)
3962 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3963 out->children[j] = loadmodel->brushq3.data_nodes + n;
3968 if (n >= loadmodel->brushq3.num_leafs)
3969 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3970 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3973 for (j = 0;j < 3;j++)
3975 // yes the mins/maxs are ints
3976 out->mins[j] = LittleLong(in->mins[j]);
3977 out->maxs[j] = LittleLong(in->maxs[j]);
3981 // set the parent pointers
3982 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3985 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3988 q3dlightgrid_t *out;
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]);
4070 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1]);
4071 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2]);
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 for (j = 0;j < 2;j++)
4084 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4085 for (i = 0;i < 2;i++)
4087 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4088 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4089 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4090 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4091 pitch = s->diffusepitch * M_PI / 128;
4092 yaw = s->diffuseyaw * M_PI / 128;
4093 sinpitch = sin(pitch);
4094 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4095 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4096 diffusenormal[2] += blend * (cos(pitch));
4097 //Con_Printf("blend %f: ambient %i %i %i, diffuse %i %i %i, diffusepitch %i diffuseyaw %i (%f %f, normal %f %f %f)\n", blend, s->ambientrgb[0], s->ambientrgb[1], s->ambientrgb[2], s->diffusergb[0], s->diffusergb[1], s->diffusergb[2], s->diffusepitch, s->diffuseyaw, pitch, yaw, (cos(yaw) * cospitch), (sin(yaw) * cospitch), (-sin(pitch)));
4101 VectorNormalize(diffusenormal);
4102 //Con_Printf("result: ambient %f %f %f diffuse %f %f %f diffusenormal %f %f %f\n", ambientcolor[0], ambientcolor[1], ambientcolor[2], diffusecolor[0], diffusecolor[1], diffusecolor[2], diffusenormal[0], diffusenormal[1], diffusenormal[2]);
4105 static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t start, const vec3_t end, vec_t startfrac, vec_t endfrac, const vec3_t linestart, const vec3_t lineend, int markframe)
4107 int i, startside, endside;
4108 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4112 if (startfrac >= trace->fraction)
4114 // note: all line fragments past first impact fraction are ignored
4115 while (node->isnode)
4117 // recurse down node sides
4118 dist1 = PlaneDiff(start, node->plane);
4119 dist2 = PlaneDiff(end, node->plane);
4120 startside = dist1 < 0;
4121 endside = dist2 < 0;
4122 if (startside == endside)
4124 // most of the time the line fragment is on one side of the plane
4125 node = node->children[startside];
4129 // line crosses node plane, split the line
4130 midfrac = dist1 / (dist1 - dist2);
4131 VectorLerp(linestart, midfrac, lineend, mid);
4132 // take the near side first
4133 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4134 if (midfrac < trace->fraction)
4135 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4140 segmentmins[0] = min(start[0], end[0]);
4141 segmentmins[1] = min(start[1], end[1]);
4142 segmentmins[2] = min(start[2], end[2]);
4143 segmentmaxs[0] = max(start[0], end[0]);
4144 segmentmaxs[1] = max(start[1], end[1]);
4145 segmentmaxs[2] = max(start[2], end[2]);
4146 leaf = (q3mleaf_t *)node;
4147 for (i = 0;i < leaf->numleafbrushes;i++)
4149 if (startfrac >= trace->fraction)
4151 brush = leaf->firstleafbrush[i]->colbrushf;
4152 if (brush && brush->markframe != markframe)
4154 brush->markframe = markframe;
4155 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4156 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4159 if (mod_q3bsp_curves_collisions.integer)
4161 for (i = 0;i < leaf->numleaffaces;i++)
4163 if (startfrac >= trace->fraction)
4165 face = leaf->firstleafface[i];
4166 if (face->collisions && face->collisionmarkframe != markframe)
4168 face->collisionmarkframe = markframe;
4169 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4170 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4176 static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs)
4179 float nodesegmentmins[3], nodesegmentmaxs[3];
4183 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4184 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4185 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4186 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4187 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4188 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4189 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4193 // recurse down node sides
4194 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4197 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4198 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4200 else if (sides == 2)
4201 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4203 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4205 dist = node->plane->dist - (1.0f / 8.0f);
4206 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4208 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4210 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4216 dist = node->plane->dist + (1.0f / 8.0f);
4217 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4219 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4221 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4227 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4229 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4231 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4237 leaf = (q3mleaf_t *)node;
4238 for (i = 0;i < leaf->numleafbrushes;i++)
4240 brush = leaf->firstleafbrush[i]->colbrushf;
4241 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4243 brush->markframe = markframe;
4244 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4247 if (mod_q3bsp_curves_collisions.integer)
4249 for (i = 0;i < leaf->numleaffaces;i++)
4251 face = leaf->firstleafface[i];
4252 // note: this can not be optimized with a face->collisionmarkframe because each triangle of the face would need to be marked as done individually (because each one is bbox culled individually), and if all are marked, then the face could be marked as done
4253 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4254 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4260 static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
4263 float segmentmins[3], segmentmaxs[3];
4264 colbrushf_t *thisbrush_start, *thisbrush_end;
4265 matrix4x4_t startmatrix, endmatrix;
4266 static int markframe = 0;
4268 memset(trace, 0, sizeof(*trace));
4269 trace->fraction = 1;
4270 trace->hitsupercontentsmask = hitsupercontentsmask;
4271 Matrix4x4_CreateIdentity(&startmatrix);
4272 Matrix4x4_CreateIdentity(&endmatrix);
4273 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4274 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4275 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4276 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4277 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4278 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4279 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4282 if (model->brushq3.submodel)
4284 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4285 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4286 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4287 if (mod_q3bsp_curves_collisions.integer)
4289 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4291 face = model->brushq3.data_thismodel->firstface + i;
4292 if (face->collisions)
4293 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4298 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4302 // box trace, performed as brush trace
4303 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4304 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4305 if (model->brushq3.submodel)
4307 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4308 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4309 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4310 if (mod_q3bsp_curves_collisions.integer)
4312 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4314 face = model->brushq3.data_thismodel->firstface + i;
4315 if (face->collisions)
4316 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4321 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4326 static int Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const q3mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4333 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4334 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4337 // node - recurse down the BSP tree
4338 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4341 node = node->children[0];
4344 node = node->children[1];
4346 default: // crossing
4347 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4349 node = node->children[1];
4356 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4358 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4361 //Returns PVS data for a given point
4362 //(note: can return NULL)
4363 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4366 Mod_CheckLoaded(model);
4367 node = model->brushq3.data_nodes;
4368 while (node->isnode)
4369 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4370 if (((q3mleaf_t *)node)->clusterindex >= 0)
4371 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4376 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4382 while (node->isnode)
4384 d = PlaneDiff(org, node->plane);
4386 node = node->children[0];
4387 else if (d < -radius)
4388 node = node->children[1];
4391 // go down both sides
4392 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4393 node = node->children[1];
4396 // if this is a leaf with a pvs, accumulate the pvs bits
4397 if (((q3mleaf_t *)node)->clusterindex >= 0)
4399 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4400 for (i = 0;i < pvsbytes;i++)
4401 pvsbuffer[i] |= pvs[i];
4406 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4407 //of the given point.
4408 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4410 int bytes = model->brushq3.num_pvschainlength;
4411 bytes = min(bytes, pvsbufferlength);
4412 memset(pvsbuffer, 0, bytes);
4413 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4418 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4420 int supercontents = 0;
4421 if (nativecontents & Q2CONTENTS_SOLID)
4422 supercontents |= SUPERCONTENTS_SOLID;
4423 if (nativecontents & Q2CONTENTS_WATER)
4424 supercontents |= SUPERCONTENTS_WATER;
4425 if (nativecontents & Q2CONTENTS_SLIME)
4426 supercontents |= SUPERCONTENTS_SLIME;
4427 if (nativecontents & Q2CONTENTS_LAVA)
4428 supercontents |= SUPERCONTENTS_LAVA;
4429 return supercontents;
4432 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4434 int nativecontents = 0;
4435 if (supercontents & SUPERCONTENTS_SOLID)
4436 nativecontents |= Q2CONTENTS_SOLID;
4437 if (supercontents & SUPERCONTENTS_WATER)
4438 nativecontents |= Q2CONTENTS_WATER;
4439 if (supercontents & SUPERCONTENTS_SLIME)
4440 nativecontents |= Q2CONTENTS_SLIME;
4441 if (supercontents & SUPERCONTENTS_LAVA)
4442 nativecontents |= Q2CONTENTS_LAVA;
4443 return nativecontents;
4446 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4447 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4448 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4449 extern void R_Q3BSP_DrawLight(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
4450 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4453 q3dheader_t *header;
4454 float corner[3], yawradius, modelradius;
4456 mod->type = mod_brushq3;
4460 header = (q3dheader_t *)buffer;
4462 i = LittleLong(header->version);
4463 if (i != Q3BSPVERSION)
4464 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4465 if (loadmodel->isworldmodel)
4467 Cvar_SetValue("halflifebsp", false);
4468 // until we get a texture for it...
4472 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4473 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4474 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4475 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4476 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4477 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4478 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4479 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4480 //mod->DrawSky = R_Q3BSP_DrawSky;
4481 mod->Draw = R_Q3BSP_Draw;
4482 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4483 mod->DrawLight = R_Q3BSP_DrawLight;
4485 mod_base = (qbyte *)header;
4487 // swap all the lumps
4488 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4489 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4491 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4492 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4493 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4494 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4495 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4496 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4497 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4498 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4499 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4500 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4501 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4502 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4503 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4504 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4505 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4506 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4507 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4508 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4510 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4517 // LordHavoc: only register submodels if it is the world
4518 // (prevents bsp models from replacing world submodels)
4519 if (!loadmodel->isworldmodel)
4521 // duplicate the basic information
4522 sprintf(name, "*%i", i);
4523 mod = Mod_FindName(name);
4525 strcpy(mod->name, name);
4526 // textures and memory belong to the main model
4527 mod->texturepool = NULL;
4528 mod->mempool = NULL;
4530 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4531 mod->brushq3.submodel = i;
4533 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4534 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4535 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4536 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4537 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4538 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4539 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4540 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4541 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4542 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4543 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4544 mod->yawmins[2] = mod->normalmins[2];
4545 mod->yawmaxs[2] = mod->normalmaxs[2];
4546 mod->radius = modelradius;
4547 mod->radius2 = modelradius * modelradius;
4551 void Mod_IBSP_Load(model_t *mod, void *buffer)
4553 int i = LittleLong(((int *)buffer)[1]);
4554 if (i == Q3BSPVERSION)
4555 Mod_Q3BSP_Load(mod,buffer);
4556 else if (i == Q2BSPVERSION)
4557 Mod_Q2BSP_Load(mod,buffer);
4559 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4562 void Mod_MAP_Load(model_t *mod, void *buffer)
4564 Host_Error("Mod_MAP_Load: not yet implemented\n");