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->DrawFakeShadow = NULL;
2756 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2757 mod->DrawLight = R_Model_Brush_DrawLight;
2758 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2759 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2760 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2761 Mod_Q1BSP_BuildPVSTextureChains(mod);
2762 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2763 if (mod->brushq1.nummodelsurfaces)
2765 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2766 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2767 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2770 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2772 // we only need to have a drawsky function if it is used(usually only on world model)
2773 if (surf->texinfo->texture->shader == &Cshader_sky)
2774 mod->DrawSky = R_Model_Brush_DrawSky;
2775 // LordHavoc: submodels always clip, even if water
2776 if (mod->brush.numsubmodels - 1)
2777 surf->flags |= SURF_SOLIDCLIP;
2778 // calculate bounding shapes
2779 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2781 for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2783 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2784 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2785 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2786 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2787 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2788 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2789 dist = vec[0]*vec[0]+vec[1]*vec[1];
2790 if (modelyawradius < dist)
2791 modelyawradius = dist;
2792 dist += vec[2]*vec[2];
2793 if (modelradius < dist)
2798 modelyawradius = sqrt(modelyawradius);
2799 modelradius = sqrt(modelradius);
2800 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2801 mod->yawmins[2] = mod->normalmins[2];
2802 mod->yawmaxs[2] = mod->normalmaxs[2];
2803 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2804 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2805 mod->radius = modelradius;
2806 mod->radius2 = modelradius * modelradius;
2810 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2811 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2813 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2815 mod->brushq1.visleafs = bm->visleafs;
2817 // LordHavoc: only register submodels if it is the world
2818 // (prevents bsp models from replacing world submodels)
2819 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2822 // duplicate the basic information
2823 sprintf(name, "*%i", i+1);
2824 loadmodel = Mod_FindName(name);
2826 strcpy(loadmodel->name, name);
2827 // textures and memory belong to the main model
2828 loadmodel->texturepool = NULL;
2829 loadmodel->mempool = NULL;
2834 loadmodel = originalloadmodel;
2835 //Mod_Q1BSP_ProcessLightList();
2838 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2842 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2849 in = (void *)(mod_base + l->fileofs);
2850 if (l->filelen % sizeof(*in))
2851 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2852 count = l->filelen / sizeof(*in);
2853 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2856 loadmodel->num = count;
2858 for (i = 0;i < count;i++, in++, out++)
2864 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2871 in = (void *)(mod_base + l->fileofs);
2872 if (l->filelen % sizeof(*in))
2873 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2874 count = l->filelen / sizeof(*in);
2875 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2878 loadmodel->num = count;
2880 for (i = 0;i < count;i++, in++, out++)
2886 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2893 in = (void *)(mod_base + l->fileofs);
2894 if (l->filelen % sizeof(*in))
2895 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2896 count = l->filelen / sizeof(*in);
2897 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2900 loadmodel->num = count;
2902 for (i = 0;i < count;i++, in++, out++)
2908 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2915 in = (void *)(mod_base + l->fileofs);
2916 if (l->filelen % sizeof(*in))
2917 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2918 count = l->filelen / sizeof(*in);
2919 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2922 loadmodel->num = count;
2924 for (i = 0;i < count;i++, in++, out++)
2930 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2937 in = (void *)(mod_base + l->fileofs);
2938 if (l->filelen % sizeof(*in))
2939 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2940 count = l->filelen / sizeof(*in);
2941 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2944 loadmodel->num = count;
2946 for (i = 0;i < count;i++, in++, out++)
2952 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2959 in = (void *)(mod_base + l->fileofs);
2960 if (l->filelen % sizeof(*in))
2961 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2962 count = l->filelen / sizeof(*in);
2963 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2966 loadmodel->num = count;
2968 for (i = 0;i < count;i++, in++, out++)
2974 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2981 in = (void *)(mod_base + l->fileofs);
2982 if (l->filelen % sizeof(*in))
2983 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2984 count = l->filelen / sizeof(*in);
2985 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2988 loadmodel->num = count;
2990 for (i = 0;i < count;i++, in++, out++)
2996 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3003 in = (void *)(mod_base + l->fileofs);
3004 if (l->filelen % sizeof(*in))
3005 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3006 count = l->filelen / sizeof(*in);
3007 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3010 loadmodel->num = count;
3012 for (i = 0;i < count;i++, in++, out++)
3018 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3025 in = (void *)(mod_base + l->fileofs);
3026 if (l->filelen % sizeof(*in))
3027 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3028 count = l->filelen / sizeof(*in);
3029 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3032 loadmodel->num = count;
3034 for (i = 0;i < count;i++, in++, out++)
3040 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3047 in = (void *)(mod_base + l->fileofs);
3048 if (l->filelen % sizeof(*in))
3049 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3050 count = l->filelen / sizeof(*in);
3051 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3054 loadmodel->num = count;
3056 for (i = 0;i < count;i++, in++, out++)
3062 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3069 in = (void *)(mod_base + l->fileofs);
3070 if (l->filelen % sizeof(*in))
3071 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3072 count = l->filelen / sizeof(*in);
3073 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3076 loadmodel->num = count;
3078 for (i = 0;i < count;i++, in++, out++)
3084 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3091 in = (void *)(mod_base + l->fileofs);
3092 if (l->filelen % sizeof(*in))
3093 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3094 count = l->filelen / sizeof(*in);
3095 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3098 loadmodel->num = count;
3100 for (i = 0;i < count;i++, in++, out++)
3106 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3113 in = (void *)(mod_base + l->fileofs);
3114 if (l->filelen % sizeof(*in))
3115 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3116 count = l->filelen / sizeof(*in);
3117 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3120 loadmodel->num = count;
3122 for (i = 0;i < count;i++, in++, out++)
3128 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3135 in = (void *)(mod_base + l->fileofs);
3136 if (l->filelen % sizeof(*in))
3137 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3138 count = l->filelen / sizeof(*in);
3139 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3142 loadmodel->num = count;
3144 for (i = 0;i < count;i++, in++, out++)
3150 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3157 in = (void *)(mod_base + l->fileofs);
3158 if (l->filelen % sizeof(*in))
3159 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3160 count = l->filelen / sizeof(*in);
3161 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3164 loadmodel->num = count;
3166 for (i = 0;i < count;i++, in++, out++)
3172 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3179 in = (void *)(mod_base + l->fileofs);
3180 if (l->filelen % sizeof(*in))
3181 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3182 count = l->filelen / sizeof(*in);
3183 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3186 loadmodel->num = count;
3188 for (i = 0;i < count;i++, in++, out++)
3194 static void Mod_Q2BSP_LoadModels(lump_t *l)
3201 in = (void *)(mod_base + l->fileofs);
3202 if (l->filelen % sizeof(*in))
3203 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3204 count = l->filelen / sizeof(*in);
3205 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3208 loadmodel->num = count;
3210 for (i = 0;i < count;i++, in++, out++)
3216 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3219 q2dheader_t *header;
3221 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3223 mod->type = mod_brushq2;
3225 header = (q2dheader_t *)buffer;
3227 i = LittleLong(header->version);
3228 if (i != Q2BSPVERSION)
3229 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3230 mod->brush.ishlbsp = false;
3231 if (loadmodel->isworldmodel)
3233 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3234 // until we get a texture for it...
3238 mod_base = (qbyte *)header;
3240 // swap all the lumps
3241 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3242 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3244 // store which lightmap format to use
3245 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3247 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3248 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3249 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3250 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3251 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3252 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3253 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3254 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3255 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3256 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3257 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3258 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3259 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3260 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3261 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3262 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3263 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3264 // LordHavoc: must go last because this makes the submodels
3265 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3268 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3269 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3271 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3274 char key[128], value[4096];
3276 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3277 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3278 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3281 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3282 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3283 data = loadmodel->brush.entities;
3284 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3285 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3289 if (!COM_ParseToken(&data, false))
3291 if (com_token[0] == '}')
3292 break; // end of worldspawn
3293 if (com_token[0] == '_')
3294 strcpy(key, com_token + 1);
3296 strcpy(key, com_token);
3297 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3298 key[strlen(key)-1] = 0;
3299 if (!COM_ParseToken(&data, false))
3301 strcpy(value, com_token);
3302 if (!strcmp("gridsize", key))
3304 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3305 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3311 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3317 in = (void *)(mod_base + l->fileofs);
3318 if (l->filelen % sizeof(*in))
3319 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3320 count = l->filelen / sizeof(*in);
3321 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3323 loadmodel->brushq3.data_textures = out;
3324 loadmodel->brushq3.num_textures = count;
3326 for (i = 0;i < count;i++, in++, out++)
3328 strncpy(out->name, in->name, sizeof(out->name) - 1);
3329 out->surfaceflags = LittleLong(in->surfaceflags);
3330 out->nativecontents = LittleLong(in->contents);
3331 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3332 out->renderflags = 0;
3333 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3334 out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3335 if (!strncmp(out->name, "textures/skies/", 15))
3336 out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY;
3339 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3343 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3349 in = (void *)(mod_base + l->fileofs);
3350 if (l->filelen % sizeof(*in))
3351 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3352 count = l->filelen / sizeof(*in);
3353 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3355 loadmodel->brushq3.data_planes = out;
3356 loadmodel->brushq3.num_planes = count;
3358 for (i = 0;i < count;i++, in++, out++)
3360 out->normal[0] = LittleLong(in->normal[0]);
3361 out->normal[1] = LittleLong(in->normal[1]);
3362 out->normal[2] = LittleLong(in->normal[2]);
3363 out->dist = LittleLong(in->dist);
3368 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3371 q3mbrushside_t *out;
3374 in = (void *)(mod_base + l->fileofs);
3375 if (l->filelen % sizeof(*in))
3376 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3377 count = l->filelen / sizeof(*in);
3378 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3380 loadmodel->brushq3.data_brushsides = out;
3381 loadmodel->brushq3.num_brushsides = count;
3383 for (i = 0;i < count;i++, in++, out++)
3385 n = LittleLong(in->planeindex);
3386 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3387 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3388 out->plane = loadmodel->brushq3.data_planes + n;
3389 n = LittleLong(in->textureindex);
3390 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3391 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3392 out->texture = loadmodel->brushq3.data_textures + n;
3396 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3400 int i, j, n, c, count, maxplanes;
3403 in = (void *)(mod_base + l->fileofs);
3404 if (l->filelen % sizeof(*in))
3405 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3406 count = l->filelen / sizeof(*in);
3407 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3409 loadmodel->brushq3.data_brushes = out;
3410 loadmodel->brushq3.num_brushes = count;
3415 for (i = 0;i < count;i++, in++, out++)
3417 n = LittleLong(in->firstbrushside);
3418 c = LittleLong(in->numbrushsides);
3419 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3420 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3421 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3422 out->numbrushsides = c;
3423 n = LittleLong(in->textureindex);
3424 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3425 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3426 out->texture = loadmodel->brushq3.data_textures + n;
3428 // make a list of mplane_t structs to construct a colbrush from
3429 if (maxplanes < out->numbrushsides)
3431 maxplanes = out->numbrushsides;
3434 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3436 for (j = 0;j < out->numbrushsides;j++)
3438 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3439 planes[j].dist = out->firstbrushside[j].plane->dist;
3441 // make the colbrush from the planes
3442 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3448 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3454 in = (void *)(mod_base + l->fileofs);
3455 if (l->filelen % sizeof(*in))
3456 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3457 count = l->filelen / sizeof(*in);
3458 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3460 loadmodel->brushq3.data_effects = out;
3461 loadmodel->brushq3.num_effects = count;
3463 for (i = 0;i < count;i++, in++, out++)
3465 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3466 n = LittleLong(in->brushindex);
3467 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3468 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3469 out->brush = loadmodel->brushq3.data_brushes + n;
3470 out->unknown = LittleLong(in->unknown);
3474 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3479 in = (void *)(mod_base + l->fileofs);
3480 if (l->filelen % sizeof(*in))
3481 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3482 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3483 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3484 loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3485 loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3486 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3487 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3488 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3489 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3491 for (i = 0;i < count;i++, in++)
3493 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3494 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3495 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3496 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3497 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3498 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3499 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3500 // svector/tvector are calculated later in face loading
3501 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3502 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3503 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3504 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3505 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3506 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3507 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3508 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3509 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3510 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3511 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3512 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3513 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3517 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3523 in = (void *)(mod_base + l->fileofs);
3524 if (l->filelen % sizeof(int[3]))
3525 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3526 count = l->filelen / sizeof(*in);
3527 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3529 loadmodel->brushq3.num_triangles = count / 3;
3530 loadmodel->brushq3.data_element3i = out;
3531 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3533 for (i = 0;i < count;i++, in++, out++)
3535 *out = LittleLong(*in);
3536 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3538 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3544 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3550 in = (void *)(mod_base + l->fileofs);
3551 if (l->filelen % sizeof(*in))
3552 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3553 count = l->filelen / sizeof(*in);
3554 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3556 loadmodel->brushq3.data_lightmaps = out;
3557 loadmodel->brushq3.num_lightmaps = count;
3559 for (i = 0;i < count;i++, in++, out++)
3560 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3563 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3567 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3568 //int *originalelement3i;
3569 //int *originalneighbor3i;
3570 float *originalvertex3f;
3571 //float *originalsvector3f;
3572 //float *originaltvector3f;
3573 //float *originalnormal3f;
3574 float *originalcolor4f;
3575 float *originaltexcoordtexture2f;
3576 float *originaltexcoordlightmap2f;
3579 in = (void *)(mod_base + l->fileofs);
3580 if (l->filelen % sizeof(*in))
3581 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3582 count = l->filelen / sizeof(*in);
3583 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3585 loadmodel->brushq3.data_faces = out;
3586 loadmodel->brushq3.num_faces = count;
3588 for (i = 0;i < count;i++, in++, out++)
3590 // check face type first
3591 out->type = LittleLong(in->type);
3592 if (out->type != Q3FACETYPE_POLYGON
3593 && out->type != Q3FACETYPE_PATCH
3594 && out->type != Q3FACETYPE_MESH
3595 && out->type != Q3FACETYPE_FLARE)
3597 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3598 out->type = 0; // error
3602 n = LittleLong(in->textureindex);
3603 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3605 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3606 out->type = 0; // error
3610 out->texture = loadmodel->brushq3.data_textures + n;
3611 n = LittleLong(in->effectindex);
3612 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3614 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3620 out->effect = loadmodel->brushq3.data_effects + n;
3621 n = LittleLong(in->lightmapindex);
3622 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3624 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3628 out->lightmaptexture = NULL;
3630 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3632 out->firstvertex = LittleLong(in->firstvertex);
3633 out->numvertices = LittleLong(in->numvertices);
3634 out->firstelement = LittleLong(in->firstelement);
3635 out->numelements = LittleLong(in->numelements);
3636 out->numtriangles = out->numelements / 3;
3637 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3639 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);
3640 out->type = 0; // error
3643 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3645 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);
3646 out->type = 0; // error
3649 if (out->numtriangles * 3 != out->numelements)
3651 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3652 out->type = 0; // error
3655 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3656 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3657 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3658 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3659 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3660 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3661 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3662 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3663 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3666 case Q3FACETYPE_POLYGON:
3667 case Q3FACETYPE_MESH:
3668 // no processing necessary
3670 case Q3FACETYPE_PATCH:
3671 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3672 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3673 if (patchsize[0] < 1 || patchsize[1] < 1)
3675 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3676 out->type = 0; // error
3679 // convert patch to Q3FACETYPE_MESH
3680 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3681 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3682 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3683 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3684 finalvertices = finalwidth * finalheight;
3685 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3686 originalvertex3f = out->data_vertex3f;
3687 //originalsvector3f = out->data_svector3f;
3688 //originaltvector3f = out->data_tvector3f;
3689 //originalnormal3f = out->data_normal3f;
3690 originalcolor4f = out->data_color4f;
3691 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3692 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3693 //originalelement3i = out->data_element3i;
3694 //originalneighbor3i = out->data_neighbor3i;
3695 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
3696 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3697 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3698 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3699 out->data_color4f = out->data_normal3f + finalvertices * 3;
3700 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3701 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3702 out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
3703 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3704 out->type = Q3FACETYPE_MESH;
3705 out->firstvertex = -1;
3706 out->numvertices = finalvertices;
3707 out->firstelement = -1;
3708 out->numtriangles = finaltriangles;
3709 out->numelements = finaltriangles * 3;
3710 // generate geometry
3711 // (note: normals are skipped because they get recalculated)
3712 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3713 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3714 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3715 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3716 // generate elements
3717 e = out->data_element3i;
3718 for (y = 0;y < finalheight - 1;y++)
3720 row0 = (y + 0) * finalwidth;
3721 row1 = (y + 1) * finalwidth;
3722 for (x = 0;x < finalwidth - 1;x++)
3734 out->numtriangles = Mod_RemoveDegenerateTriangles(out->numtriangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3735 if (developer.integer)
3737 if (out->numtriangles < finaltriangles)
3738 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);
3740 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->numvertices, out->numtriangles);
3742 // q3map does not put in collision brushes for curves... ugh
3743 out->collisions = true;
3745 case Q3FACETYPE_FLARE:
3746 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3748 out->numtriangles = 0;
3751 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3752 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3754 if (invalidelements)
3756 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);
3757 for (j = 0;j < out->numelements;j++)
3759 Con_Printf(" %i", out->data_element3i[j]);
3760 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3761 out->data_element3i[j] = 0;
3765 // for shadow volumes
3766 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->numtriangles);
3767 // for per pixel lighting
3768 Mod_BuildTextureVectorsAndNormals(out->numvertices, out->numtriangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
3769 // calculate a bounding box
3770 VectorClear(out->mins);
3771 VectorClear(out->maxs);
3772 if (out->numvertices)
3774 VectorCopy(out->data_vertex3f, out->mins);
3775 VectorCopy(out->data_vertex3f, out->maxs);
3776 for (j = 1, v = out->data_vertex3f + 3;j < out->numvertices;j++, v += 3)
3778 out->mins[0] = min(out->mins[0], v[0]);
3779 out->maxs[0] = max(out->maxs[0], v[0]);
3780 out->mins[1] = min(out->mins[1], v[1]);
3781 out->maxs[1] = max(out->maxs[1], v[1]);
3782 out->mins[2] = min(out->mins[2], v[2]);
3783 out->maxs[2] = max(out->maxs[2], v[2]);
3785 out->mins[0] -= 1.0f;
3786 out->mins[1] -= 1.0f;
3787 out->mins[2] -= 1.0f;
3788 out->maxs[0] += 1.0f;
3789 out->maxs[1] += 1.0f;
3790 out->maxs[2] += 1.0f;
3795 static void Mod_Q3BSP_LoadModels(lump_t *l)
3799 int i, j, n, c, count;
3801 in = (void *)(mod_base + l->fileofs);
3802 if (l->filelen % sizeof(*in))
3803 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3804 count = l->filelen / sizeof(*in);
3805 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3807 loadmodel->brushq3.data_models = out;
3808 loadmodel->brushq3.num_models = count;
3810 for (i = 0;i < count;i++, in++, out++)
3812 for (j = 0;j < 3;j++)
3814 out->mins[j] = LittleFloat(in->mins[j]);
3815 out->maxs[j] = LittleFloat(in->maxs[j]);
3817 n = LittleLong(in->firstface);
3818 c = LittleLong(in->numfaces);
3819 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3820 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3821 out->firstface = loadmodel->brushq3.data_faces + n;
3823 n = LittleLong(in->firstbrush);
3824 c = LittleLong(in->numbrushes);
3825 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3826 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3827 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3828 out->numbrushes = c;
3832 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3838 in = (void *)(mod_base + l->fileofs);
3839 if (l->filelen % sizeof(*in))
3840 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3841 count = l->filelen / sizeof(*in);
3842 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3844 loadmodel->brushq3.data_leafbrushes = out;
3845 loadmodel->brushq3.num_leafbrushes = count;
3847 for (i = 0;i < count;i++, in++, out++)
3849 n = LittleLong(*in);
3850 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3851 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3852 *out = loadmodel->brushq3.data_brushes + n;
3856 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3862 in = (void *)(mod_base + l->fileofs);
3863 if (l->filelen % sizeof(*in))
3864 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3865 count = l->filelen / sizeof(*in);
3866 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3868 loadmodel->brushq3.data_leaffaces = out;
3869 loadmodel->brushq3.num_leaffaces = count;
3871 for (i = 0;i < count;i++, in++, out++)
3873 n = LittleLong(*in);
3874 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3875 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3876 *out = loadmodel->brushq3.data_faces + n;
3880 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3884 int i, j, n, c, count;
3886 in = (void *)(mod_base + l->fileofs);
3887 if (l->filelen % sizeof(*in))
3888 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3889 count = l->filelen / sizeof(*in);
3890 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3892 loadmodel->brushq3.data_leafs = out;
3893 loadmodel->brushq3.num_leafs = count;
3895 for (i = 0;i < count;i++, in++, out++)
3897 out->isnode = false;
3899 out->clusterindex = LittleLong(in->clusterindex);
3900 out->areaindex = LittleLong(in->areaindex);
3901 for (j = 0;j < 3;j++)
3903 // yes the mins/maxs are ints
3904 out->mins[j] = LittleLong(in->mins[j]);
3905 out->maxs[j] = LittleLong(in->maxs[j]);
3907 n = LittleLong(in->firstleafface);
3908 c = LittleLong(in->numleaffaces);
3909 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3910 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3911 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3912 out->numleaffaces = c;
3913 n = LittleLong(in->firstleafbrush);
3914 c = LittleLong(in->numleafbrushes);
3915 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3916 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3917 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3918 out->numleafbrushes = c;
3922 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3925 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3926 node->parent = parent;
3929 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3930 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3934 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3940 in = (void *)(mod_base + l->fileofs);
3941 if (l->filelen % sizeof(*in))
3942 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3943 count = l->filelen / sizeof(*in);
3944 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3946 loadmodel->brushq3.data_nodes = out;
3947 loadmodel->brushq3.num_nodes = count;
3949 for (i = 0;i < count;i++, in++, out++)
3953 n = LittleLong(in->planeindex);
3954 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3955 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3956 out->plane = loadmodel->brushq3.data_planes + n;
3957 for (j = 0;j < 2;j++)
3959 n = LittleLong(in->childrenindex[j]);
3962 if (n >= loadmodel->brushq3.num_nodes)
3963 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3964 out->children[j] = loadmodel->brushq3.data_nodes + n;
3969 if (n >= loadmodel->brushq3.num_leafs)
3970 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3971 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3974 for (j = 0;j < 3;j++)
3976 // yes the mins/maxs are ints
3977 out->mins[j] = LittleLong(in->mins[j]);
3978 out->maxs[j] = LittleLong(in->maxs[j]);
3982 // set the parent pointers
3983 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3986 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3989 q3dlightgrid_t *out;
3992 in = (void *)(mod_base + l->fileofs);
3993 if (l->filelen % sizeof(*in))
3994 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
3995 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
3996 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
3997 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
3998 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3999 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4000 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4001 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4002 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4003 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4004 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4005 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4006 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4007 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4008 if (l->filelen < count * (int)sizeof(*in))
4009 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]);
4010 if (l->filelen != count * (int)sizeof(*in))
4011 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4013 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4014 loadmodel->brushq3.data_lightgrid = out;
4015 loadmodel->brushq3.num_lightgrid = count;
4017 // no swapping or validation necessary
4018 memcpy(out, in, count * (int)sizeof(*out));
4020 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]);
4021 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]);
4024 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4029 if (l->filelen == 0)
4032 in = (void *)(mod_base + l->fileofs);
4034 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4036 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4037 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4038 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4039 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4040 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4041 if (l->filelen < totalchains + (int)sizeof(*in))
4042 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);
4044 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4045 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4048 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4050 // FIXME: finish this code
4051 VectorCopy(in, out);
4054 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4056 int i, j, k, index[3];
4057 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4058 q3dlightgrid_t *a, *s;
4059 // FIXME: write this
4060 if (!model->brushq3.num_lightgrid)
4062 ambientcolor[0] += 128;
4063 ambientcolor[1] += 128;
4064 ambientcolor[2] += 128;
4067 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4068 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4069 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4070 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0]);
4071 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1]);
4072 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2]);
4073 index[0] = (int)floor(transformed[0]);
4074 index[1] = (int)floor(transformed[1]);
4075 index[2] = (int)floor(transformed[2]);
4076 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4077 // now lerp the values
4078 VectorClear(diffusenormal);
4079 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4080 for (k = 0;k < 2;k++)
4082 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4083 for (j = 0;j < 2;j++)
4085 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4086 for (i = 0;i < 2;i++)
4088 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4089 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4090 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4091 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4092 pitch = s->diffusepitch * M_PI / 128;
4093 yaw = s->diffuseyaw * M_PI / 128;
4094 sinpitch = sin(pitch);
4095 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4096 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4097 diffusenormal[2] += blend * (cos(pitch));
4098 //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)));
4102 VectorNormalize(diffusenormal);
4103 //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]);
4106 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)
4108 int i, startside, endside;
4109 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4113 if (startfrac >= trace->fraction)
4115 // note: all line fragments past first impact fraction are ignored
4116 while (node->isnode)
4118 // recurse down node sides
4119 dist1 = PlaneDiff(start, node->plane);
4120 dist2 = PlaneDiff(end, node->plane);
4121 startside = dist1 < 0;
4122 endside = dist2 < 0;
4123 if (startside == endside)
4125 // most of the time the line fragment is on one side of the plane
4126 node = node->children[startside];
4130 // line crosses node plane, split the line
4131 midfrac = dist1 / (dist1 - dist2);
4132 VectorLerp(linestart, midfrac, lineend, mid);
4133 // take the near side first
4134 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4135 if (midfrac < trace->fraction)
4136 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4141 segmentmins[0] = min(start[0], end[0]);
4142 segmentmins[1] = min(start[1], end[1]);
4143 segmentmins[2] = min(start[2], end[2]);
4144 segmentmaxs[0] = max(start[0], end[0]);
4145 segmentmaxs[1] = max(start[1], end[1]);
4146 segmentmaxs[2] = max(start[2], end[2]);
4147 leaf = (q3mleaf_t *)node;
4148 for (i = 0;i < leaf->numleafbrushes;i++)
4150 if (startfrac >= trace->fraction)
4152 brush = leaf->firstleafbrush[i]->colbrushf;
4153 if (brush && brush->markframe != markframe)
4155 brush->markframe = markframe;
4156 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4157 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4160 if (mod_q3bsp_curves_collisions.integer)
4162 for (i = 0;i < leaf->numleaffaces;i++)
4164 if (startfrac >= trace->fraction)
4166 face = leaf->firstleafface[i];
4167 if (face->collisions && face->collisionmarkframe != markframe)
4169 face->collisionmarkframe = markframe;
4170 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4171 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4177 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)
4180 float nodesegmentmins[3], nodesegmentmaxs[3];
4184 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4185 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4186 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4187 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4188 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4189 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4190 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4194 // recurse down node sides
4195 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4198 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4199 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4201 else if (sides == 2)
4202 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4204 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4206 dist = node->plane->dist - (1.0f / 8.0f);
4207 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4209 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4211 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4217 dist = node->plane->dist + (1.0f / 8.0f);
4218 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4220 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4222 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4228 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4230 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4232 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4238 leaf = (q3mleaf_t *)node;
4239 for (i = 0;i < leaf->numleafbrushes;i++)
4241 brush = leaf->firstleafbrush[i]->colbrushf;
4242 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4244 brush->markframe = markframe;
4245 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4248 if (mod_q3bsp_curves_collisions.integer)
4250 for (i = 0;i < leaf->numleaffaces;i++)
4252 face = leaf->firstleafface[i];
4253 // 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
4254 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4255 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4261 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)
4264 float segmentmins[3], segmentmaxs[3];
4265 colbrushf_t *thisbrush_start, *thisbrush_end;
4266 matrix4x4_t startmatrix, endmatrix;
4267 static int markframe = 0;
4269 memset(trace, 0, sizeof(*trace));
4270 trace->fraction = 1;
4271 trace->hitsupercontentsmask = hitsupercontentsmask;
4272 Matrix4x4_CreateIdentity(&startmatrix);
4273 Matrix4x4_CreateIdentity(&endmatrix);
4274 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4275 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4276 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4277 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4278 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4279 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4280 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4283 if (model->brushq3.submodel)
4285 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4286 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4287 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4288 if (mod_q3bsp_curves_collisions.integer)
4290 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4292 face = model->brushq3.data_thismodel->firstface + i;
4293 if (face->collisions)
4294 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4299 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4303 // box trace, performed as brush trace
4304 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4305 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4306 if (model->brushq3.submodel)
4308 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4309 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4310 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4311 if (mod_q3bsp_curves_collisions.integer)
4313 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4315 face = model->brushq3.data_thismodel->firstface + i;
4316 if (face->collisions)
4317 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4322 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4327 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)
4334 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4335 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4338 // node - recurse down the BSP tree
4339 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4342 node = node->children[0];
4345 node = node->children[1];
4347 default: // crossing
4348 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4350 node = node->children[1];
4357 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4359 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4362 //Returns PVS data for a given point
4363 //(note: can return NULL)
4364 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4367 Mod_CheckLoaded(model);
4368 node = model->brushq3.data_nodes;
4369 while (node->isnode)
4370 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4371 if (((q3mleaf_t *)node)->clusterindex >= 0)
4372 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4377 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4383 while (node->isnode)
4385 d = PlaneDiff(org, node->plane);
4387 node = node->children[0];
4388 else if (d < -radius)
4389 node = node->children[1];
4392 // go down both sides
4393 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4394 node = node->children[1];
4397 // if this is a leaf with a pvs, accumulate the pvs bits
4398 if (((q3mleaf_t *)node)->clusterindex >= 0)
4400 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4401 for (i = 0;i < pvsbytes;i++)
4402 pvsbuffer[i] |= pvs[i];
4407 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4408 //of the given point.
4409 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4411 int bytes = model->brushq3.num_pvschainlength;
4412 bytes = min(bytes, pvsbufferlength);
4413 memset(pvsbuffer, 0, bytes);
4414 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4419 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4421 int supercontents = 0;
4422 if (nativecontents & Q2CONTENTS_SOLID)
4423 supercontents |= SUPERCONTENTS_SOLID;
4424 if (nativecontents & Q2CONTENTS_WATER)
4425 supercontents |= SUPERCONTENTS_WATER;
4426 if (nativecontents & Q2CONTENTS_SLIME)
4427 supercontents |= SUPERCONTENTS_SLIME;
4428 if (nativecontents & Q2CONTENTS_LAVA)
4429 supercontents |= SUPERCONTENTS_LAVA;
4430 return supercontents;
4433 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4435 int nativecontents = 0;
4436 if (supercontents & SUPERCONTENTS_SOLID)
4437 nativecontents |= Q2CONTENTS_SOLID;
4438 if (supercontents & SUPERCONTENTS_WATER)
4439 nativecontents |= Q2CONTENTS_WATER;
4440 if (supercontents & SUPERCONTENTS_SLIME)
4441 nativecontents |= Q2CONTENTS_SLIME;
4442 if (supercontents & SUPERCONTENTS_LAVA)
4443 nativecontents |= Q2CONTENTS_LAVA;
4444 return nativecontents;
4447 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4448 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4449 //extern void R_Q3BSP_DrawFakeShadow(struct entity_render_s *ent);
4450 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4451 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);
4452 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4455 q3dheader_t *header;
4456 float corner[3], yawradius, modelradius;
4458 mod->type = mod_brushq3;
4462 header = (q3dheader_t *)buffer;
4464 i = LittleLong(header->version);
4465 if (i != Q3BSPVERSION)
4466 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4467 if (loadmodel->isworldmodel)
4469 Cvar_SetValue("halflifebsp", false);
4470 // until we get a texture for it...
4474 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4475 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4476 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4477 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4478 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4479 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4480 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4481 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4482 //mod->DrawSky = R_Q3BSP_DrawSky;
4483 mod->Draw = R_Q3BSP_Draw;
4484 //mod->DrawFakeShadow = R_Q3BSP_DrawFakeShadow;
4485 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4486 mod->DrawLight = R_Q3BSP_DrawLight;
4488 mod_base = (qbyte *)header;
4490 // swap all the lumps
4491 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4492 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4494 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4495 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4496 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4497 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4498 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4499 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4500 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4501 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4502 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4503 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4504 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4505 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4506 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4507 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4508 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4509 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4510 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4511 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4513 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4520 // LordHavoc: only register submodels if it is the world
4521 // (prevents bsp models from replacing world submodels)
4522 if (!loadmodel->isworldmodel)
4524 // duplicate the basic information
4525 sprintf(name, "*%i", i);
4526 mod = Mod_FindName(name);
4528 strcpy(mod->name, name);
4529 // textures and memory belong to the main model
4530 mod->texturepool = NULL;
4531 mod->mempool = NULL;
4533 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4534 mod->brushq3.submodel = i;
4536 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4537 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4538 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4539 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4540 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4541 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4542 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4543 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4544 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4545 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4546 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4547 mod->yawmins[2] = mod->normalmins[2];
4548 mod->yawmaxs[2] = mod->normalmaxs[2];
4549 mod->radius = modelradius;
4550 mod->radius2 = modelradius * modelradius;
4554 void Mod_IBSP_Load(model_t *mod, void *buffer)
4556 int i = LittleLong(((int *)buffer)[1]);
4557 if (i == Q3BSPVERSION)
4558 Mod_Q3BSP_Load(mod,buffer);
4559 else if (i == Q2BSPVERSION)
4560 Mod_Q2BSP_Load(mod,buffer);
4562 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4565 void Mod_MAP_Load(model_t *mod, void *buffer)
4567 Host_Error("Mod_MAP_Load: not yet implemented\n");