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", "0"};
41 void Mod_BrushInit(void)
43 // Cvar_RegisterVariable(&r_subdivide_size);
44 Cvar_RegisterVariable(&halflifebsp);
45 Cvar_RegisterVariable(&r_novis);
46 Cvar_RegisterVariable(&r_miplightmaps);
47 Cvar_RegisterVariable(&r_lightmaprgba);
48 Cvar_RegisterVariable(&r_nosurftextures);
49 Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
50 Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
51 Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
52 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
62 Mod_CheckLoaded(model);
64 // LordHavoc: modified to start at first clip node,
65 // in other words: first node of the (sub)model
66 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
67 while (node->contents == 0)
68 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
70 return (mleaf_t *)node;
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
77 leaf = Mod_Q1BSP_PointInLeaf(model, p);
80 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
83 memcpy(out, leaf->ambient_sound_level, i);
89 memset(out, 0, outsize);
93 static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
97 if (node->contents < 0)
100 if (node->contents == CONTENTS_SOLID)
102 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
106 // node - recurse down the BSP tree
107 switch (BoxOnPlaneSide(mins, maxs, node->plane))
110 node = node->children[0];
113 node = node->children[1];
116 if (node->children[0]->contents != CONTENTS_SOLID)
117 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
119 node = node->children[1];
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
128 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
137 return CONTENTS_EMPTY;
139 Mod_CheckLoaded(model);
141 // LordHavoc: modified to start at first clip node,
142 // in other words: first node of the (sub)model
143 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
144 while (node->contents == 0)
145 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
147 return ((mleaf_t *)node)->contents;
151 typedef struct findnonsolidlocationinfo_s
159 findnonsolidlocationinfo_t;
162 extern cvar_t samelevel;
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
166 int i, surfnum, k, *tri, *mark;
167 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
172 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
174 surf = info->model->brushq1.surfaces + *mark;
175 if (surf->flags & SURF_SOLIDCLIP)
178 VectorCopy(surf->plane->normal, surfnormal);
179 if (surf->flags & SURF_PLANEBACK)
180 VectorNegate(surfnormal, surfnormal);
182 for (k = 0;k < surf->mesh.num_triangles;k++)
184 tri = surf->mesh.data_element3i + k * 3;
185 VectorCopy((surf->mesh.data_vertex3f + tri[0] * 3), vert[0]);
186 VectorCopy((surf->mesh.data_vertex3f + tri[1] * 3), vert[1]);
187 VectorCopy((surf->mesh.data_vertex3f + tri[2] * 3), vert[2]);
188 VectorSubtract(vert[1], vert[0], edge[0]);
189 VectorSubtract(vert[2], vert[1], edge[1]);
190 CrossProduct(edge[1], edge[0], facenormal);
191 if (facenormal[0] || facenormal[1] || facenormal[2])
193 VectorNormalize(facenormal);
195 if (VectorDistance(facenormal, surfnormal) > 0.01f)
196 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
198 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
199 if (f <= info->bestdist && f >= -info->bestdist)
201 VectorSubtract(vert[0], vert[2], edge[2]);
202 VectorNormalize(edge[0]);
203 VectorNormalize(edge[1]);
204 VectorNormalize(edge[2]);
205 CrossProduct(facenormal, edge[0], edgenormal[0]);
206 CrossProduct(facenormal, edge[1], edgenormal[1]);
207 CrossProduct(facenormal, edge[2], edgenormal[2]);
209 if (samelevel.integer & 1)
210 VectorNegate(edgenormal[0], edgenormal[0]);
211 if (samelevel.integer & 2)
212 VectorNegate(edgenormal[1], edgenormal[1]);
213 if (samelevel.integer & 4)
214 VectorNegate(edgenormal[2], edgenormal[2]);
215 for (i = 0;i < 3;i++)
216 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
217 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
218 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
219 Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]);
222 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
223 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
224 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
226 // we got lucky, the center is within the face
227 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
231 if (info->bestdist > dist)
233 info->bestdist = dist;
234 VectorScale(facenormal, (info->radius - -dist), info->nudge);
239 if (info->bestdist > dist)
241 info->bestdist = dist;
242 VectorScale(facenormal, (info->radius - dist), info->nudge);
248 // check which edge or vertex the center is nearest
249 for (i = 0;i < 3;i++)
251 f = DotProduct(info->center, edge[i]);
252 if (f >= DotProduct(vert[0], edge[i])
253 && f <= DotProduct(vert[1], edge[i]))
256 VectorMA(info->center, -f, edge[i], point);
257 dist = sqrt(DotProduct(point, point));
258 if (info->bestdist > dist)
260 info->bestdist = dist;
261 VectorScale(point, (info->radius / dist), info->nudge);
263 // skip both vertex checks
264 // (both are further away than this edge)
269 // not on edge, check first vertex of edge
270 VectorSubtract(info->center, vert[i], point);
271 dist = sqrt(DotProduct(point, point));
272 if (info->bestdist > dist)
274 info->bestdist = dist;
275 VectorScale(point, (info->radius / dist), info->nudge);
287 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
291 if (((mleaf_t *)node)->nummarksurfaces)
292 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
296 float f = PlaneDiff(info->center, node->plane);
297 if (f >= -info->bestdist)
298 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
299 if (f <= info->bestdist)
300 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
304 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
307 findnonsolidlocationinfo_t info;
313 VectorCopy(in, info.center);
314 info.radius = radius;
319 VectorClear(info.nudge);
320 info.bestdist = radius;
321 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
322 VectorAdd(info.center, info.nudge, info.center);
324 while (info.bestdist < radius && ++i < 10);
325 VectorCopy(info.center, out);
328 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
330 switch(nativecontents)
335 return SUPERCONTENTS_SOLID;
337 return SUPERCONTENTS_WATER;
339 return SUPERCONTENTS_SLIME;
341 return SUPERCONTENTS_LAVA;
343 return SUPERCONTENTS_SKY;
348 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
350 if (supercontents & SUPERCONTENTS_SOLID)
351 return CONTENTS_SOLID;
352 if (supercontents & SUPERCONTENTS_SKY)
354 if (supercontents & SUPERCONTENTS_LAVA)
355 return CONTENTS_LAVA;
356 if (supercontents & SUPERCONTENTS_SLIME)
357 return CONTENTS_SLIME;
358 if (supercontents & SUPERCONTENTS_WATER)
359 return CONTENTS_WATER;
360 return CONTENTS_EMPTY;
365 // the hull we're tracing through
368 // the trace structure to fill in
371 // start, end, and end - start (in model space)
376 RecursiveHullCheckTraceInfo_t;
378 // 1/32 epsilon to keep floating point happy
379 #define DIST_EPSILON (0.03125)
381 #define HULLCHECKSTATE_EMPTY 0
382 #define HULLCHECKSTATE_SOLID 1
383 #define HULLCHECKSTATE_DONE 2
385 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
387 // status variables, these don't need to be saved on the stack when
388 // recursing... but are because this should be thread-safe
389 // (note: tracing against a bbox is not thread-safe, yet)
394 // variables that need to be stored on the stack when recursing
399 // LordHavoc: a goto! everyone flee in terror... :)
404 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
405 if (!t->trace->startfound)
407 t->trace->startfound = true;
408 t->trace->startsupercontents |= num;
410 if (num & SUPERCONTENTS_LIQUIDSMASK)
411 t->trace->inwater = true;
413 t->trace->inopen = true;
414 if (num & t->trace->hitsupercontentsmask)
416 // if the first leaf is solid, set startsolid
417 if (t->trace->allsolid)
418 t->trace->startsolid = true;
419 #ifdef COLLISIONPARANOID
422 return HULLCHECKSTATE_SOLID;
426 t->trace->allsolid = false;
427 #ifdef COLLISIONPARANOID
430 return HULLCHECKSTATE_EMPTY;
434 // find the point distances
435 node = t->hull->clipnodes + num;
437 plane = t->hull->planes + node->planenum;
440 t1 = p1[plane->type] - plane->dist;
441 t2 = p2[plane->type] - plane->dist;
445 t1 = DotProduct (plane->normal, p1) - plane->dist;
446 t2 = DotProduct (plane->normal, p2) - plane->dist;
453 #ifdef COLLISIONPARANOID
456 num = node->children[1];
465 #ifdef COLLISIONPARANOID
468 num = node->children[0];
474 // the line intersects, find intersection point
475 // LordHavoc: this uses the original trace for maximum accuracy
476 #ifdef COLLISIONPARANOID
481 t1 = t->start[plane->type] - plane->dist;
482 t2 = t->end[plane->type] - plane->dist;
486 t1 = DotProduct (plane->normal, t->start) - plane->dist;
487 t2 = DotProduct (plane->normal, t->end) - plane->dist;
490 midf = t1 / (t1 - t2);
491 midf = bound(p1f, midf, p2f);
492 VectorMA(t->start, midf, t->dist, mid);
494 // recurse both sides, front side first
495 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
496 // if this side is not empty, return what it is (solid or done)
497 if (ret != HULLCHECKSTATE_EMPTY)
500 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
501 // if other side is not solid, return what it is (empty or done)
502 if (ret != HULLCHECKSTATE_SOLID)
505 // front is air and back is solid, this is the impact point...
508 t->trace->plane.dist = -plane->dist;
509 VectorNegate (plane->normal, t->trace->plane.normal);
513 t->trace->plane.dist = plane->dist;
514 VectorCopy (plane->normal, t->trace->plane.normal);
517 // bias away from surface a bit
518 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
519 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
521 midf = t1 / (t1 - t2);
522 t->trace->fraction = bound(0.0f, midf, 1.0);
524 #ifdef COLLISIONPARANOID
527 return HULLCHECKSTATE_DONE;
530 #ifndef COLLISIONPARANOID
531 static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
534 num = t->hull->clipnodes[num].children[(t->hull->planes[t->hull->clipnodes[num].planenum].type < 3 ? t->start[t->hull->planes[t->hull->clipnodes[num].planenum].type] : DotProduct(t->hull->planes[t->hull->clipnodes[num].planenum].normal, t->start)) < t->hull->planes[t->hull->clipnodes[num].planenum].dist];
535 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
536 t->trace->startsupercontents |= num;
537 if (num & SUPERCONTENTS_LIQUIDSMASK)
538 t->trace->inwater = true;
540 t->trace->inopen = true;
541 if (num & t->trace->hitsupercontentsmask)
543 t->trace->allsolid = t->trace->startsolid = true;
544 return HULLCHECKSTATE_SOLID;
548 t->trace->allsolid = t->trace->startsolid = false;
549 return HULLCHECKSTATE_EMPTY;
554 static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
556 // this function currently only supports same size start and end
558 RecursiveHullCheckTraceInfo_t rhc;
560 memset(&rhc, 0, sizeof(rhc));
561 memset(trace, 0, sizeof(trace_t));
563 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
564 rhc.trace->fraction = 1;
565 rhc.trace->allsolid = true;
566 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
568 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
569 else if (model->brush.ishlbsp)
571 // LordHavoc: this has to have a minor tolerance (the .1) because of
572 // minor float precision errors from the box being transformed around
573 if (boxsize[0] < 32.1)
575 if (boxsize[2] < 54) // pick the nearest of 36 or 72
576 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
578 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
581 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
585 // LordHavoc: this has to have a minor tolerance (the .1) because of
586 // minor float precision errors from the box being transformed around
587 if (boxsize[0] < 32.1)
588 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
590 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
592 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
593 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
594 VectorSubtract(rhc.end, rhc.start, rhc.dist);
595 #ifdef COLLISIONPARANOID
596 Con_Printf("t(%f %f %f,%f %f %f,%i %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]);
597 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
600 if (DotProduct(rhc.dist, rhc.dist))
601 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
603 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
607 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)
609 int side, distz = endz - startz;
614 if (node->contents < 0)
615 return false; // didn't hit anything
617 switch (node->plane->type)
620 node = node->children[x < node->plane->dist];
623 node = node->children[y < node->plane->dist];
626 side = startz < node->plane->dist;
627 if ((endz < node->plane->dist) == side)
629 node = node->children[side];
632 // found an intersection
633 mid = node->plane->dist;
636 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
637 front += startz * node->plane->normal[2];
638 back += endz * node->plane->normal[2];
639 side = front < node->plane->dist;
640 if ((back < node->plane->dist) == side)
642 node = node->children[side];
645 // found an intersection
646 mid = startz + distz * (front - node->plane->dist) / (front - back);
650 // go down front side
651 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
652 return true; // hit something
655 // check for impact on this node
656 if (node->numsurfaces)
661 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
662 for (i = 0;i < node->numsurfaces;i++, surf++)
664 if (!(surf->flags & SURF_LIGHTMAP))
665 continue; // no lightmaps
667 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]);
668 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]);
670 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
673 ds -= surf->texturemins[0];
674 dt -= surf->texturemins[1];
676 if (ds > surf->extents[0] || dt > surf->extents[1])
682 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;
683 line3 = ((surf->extents[0]>>4)+1)*3;
684 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
686 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
688 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
690 scale = d_lightstylevalue[surf->styles[maps]];
691 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
692 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
693 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
694 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
699 LordHavoc: here's the readable version of the interpolation
700 code, not quite as easy for the compiler to optimize...
702 dsfrac is the X position in the lightmap pixel, * 16
703 dtfrac is the Y position in the lightmap pixel, * 16
704 r00 is top left corner, r01 is top right corner
705 r10 is bottom left corner, r11 is bottom right corner
706 g and b are the same layout.
707 r0 and r1 are the top and bottom intermediate results
709 first we interpolate the top two points, to get the top
712 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
713 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
714 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
716 then we interpolate the bottom two points, to get the
719 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
720 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
721 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
723 then we interpolate the top and bottom samples to get the
724 middle sample (the one which was requested)
726 r = (((r1-r0) * dtfrac) >> 4) + r0;
727 g = (((g1-g0) * dtfrac) >> 4) + g0;
728 b = (((b1-b0) * dtfrac) >> 4) + b0;
731 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
732 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
733 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
735 return true; // success
740 node = node->children[side ^ 1];
742 distz = endz - startz;
747 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
749 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);
752 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
759 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
769 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
772 for (c = *in++;c > 0;c--)
776 Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\"\n", loadmodel->name);
785 static void Mod_Q1BSP_LoadTextures(lump_t *l)
787 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
789 texture_t *tx, *tx2, *anims[10], *altanims[10];
791 qbyte *data, *mtdata;
794 loadmodel->brushq1.textures = NULL;
796 // add two slots for notexture walls and notexture liquids
799 m = (dmiptexlump_t *)(mod_base + l->fileofs);
800 m->nummiptex = LittleLong (m->nummiptex);
801 loadmodel->brushq1.numtextures = m->nummiptex + 2;
806 loadmodel->brushq1.numtextures = 2;
809 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
811 // fill out all slots with notexture
812 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
815 strcpy(tx->name, "NO TEXTURE FOUND");
818 tx->skin.base = r_notexture;
819 tx->shader = &Cshader_wall_lightmap;
820 tx->flags = SURF_SOLIDCLIP;
821 if (i == loadmodel->brushq1.numtextures - 1)
823 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
824 tx->shader = &Cshader_water;
826 tx->currentframe = tx;
832 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
834 // LordHavoc: mostly rewritten map texture loader
835 for (i = 0;i < m->nummiptex;i++)
837 dofs[i] = LittleLong(dofs[i]);
838 if (dofs[i] == -1 || r_nosurftextures.integer)
840 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
842 // make sure name is no more than 15 characters
843 for (j = 0;dmiptex->name[j] && j < 15;j++)
844 name[j] = dmiptex->name[j];
847 mtwidth = LittleLong(dmiptex->width);
848 mtheight = LittleLong(dmiptex->height);
850 j = LittleLong(dmiptex->offsets[0]);
854 if (j < 40 || j + mtwidth * mtheight > l->filelen)
856 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
859 mtdata = (qbyte *)dmiptex + j;
862 if ((mtwidth & 15) || (mtheight & 15))
863 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
865 // LordHavoc: force all names to lowercase
866 for (j = 0;name[j];j++)
867 if (name[j] >= 'A' && name[j] <= 'Z')
868 name[j] += 'a' - 'A';
870 tx = loadmodel->brushq1.textures + i;
871 strcpy(tx->name, name);
873 tx->height = mtheight;
877 sprintf(tx->name, "unnamed%i", i);
878 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
881 // LordHavoc: HL sky textures are entirely different than quake
882 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
884 if (loadmodel->isworldmodel)
886 data = loadimagepixels(tx->name, false, 0, 0);
889 if (image_width == 256 && image_height == 128)
897 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
899 R_InitSky(mtdata, 1);
902 else if (mtdata != NULL)
903 R_InitSky(mtdata, 1);
908 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
910 // did not find external texture, load it from the bsp or wad3
911 if (loadmodel->brush.ishlbsp)
913 // internal texture overrides wad
914 qbyte *pixels, *freepixels, *fogpixels;
915 pixels = freepixels = NULL;
917 pixels = W_ConvertWAD3Texture(dmiptex);
919 pixels = freepixels = W_GetTexture(tx->name);
922 tx->width = image_width;
923 tx->height = image_height;
924 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);
925 if (Image_CheckAlpha(pixels, image_width * image_height, true))
927 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
928 for (j = 0;j < image_width * image_height * 4;j += 4)
930 fogpixels[j + 0] = 255;
931 fogpixels[j + 1] = 255;
932 fogpixels[j + 2] = 255;
933 fogpixels[j + 3] = pixels[j + 3];
935 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
940 Mem_Free(freepixels);
942 else if (mtdata) // texture included
943 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
946 if (tx->skin.base == NULL)
951 tx->skin.base = r_notexture;
954 if (tx->name[0] == '*')
956 // turb does not block movement
957 tx->flags &= ~SURF_SOLIDCLIP;
958 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
959 // LordHavoc: some turbulent textures should be fullbright and solid
960 if (!strncmp(tx->name,"*lava",5)
961 || !strncmp(tx->name,"*teleport",9)
962 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
963 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
965 tx->flags |= SURF_WATERALPHA;
966 tx->shader = &Cshader_water;
968 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
970 tx->flags |= SURF_DRAWSKY;
971 tx->shader = &Cshader_sky;
975 tx->flags |= SURF_LIGHTMAP;
977 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
978 tx->shader = &Cshader_wall_lightmap;
981 // start out with no animation
982 tx->currentframe = tx;
985 // sequence the animations
986 for (i = 0;i < m->nummiptex;i++)
988 tx = loadmodel->brushq1.textures + i;
989 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
991 if (tx->anim_total[0] || tx->anim_total[1])
992 continue; // already sequenced
994 // find the number of frames in the animation
995 memset(anims, 0, sizeof(anims));
996 memset(altanims, 0, sizeof(altanims));
998 for (j = i;j < m->nummiptex;j++)
1000 tx2 = loadmodel->brushq1.textures + j;
1001 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
1005 if (num >= '0' && num <= '9')
1006 anims[num - '0'] = tx2;
1007 else if (num >= 'a' && num <= 'j')
1008 altanims[num - 'a'] = tx2;
1010 Con_Printf("Bad animating texture %s\n", tx->name);
1014 for (j = 0;j < 10;j++)
1021 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
1024 for (j = 0;j < max;j++)
1028 Con_Printf("Missing frame %i of %s\n", j, tx->name);
1032 for (j = 0;j < altmax;j++)
1036 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
1045 // if there is no alternate animation, duplicate the primary
1046 // animation into the alternate
1048 for (k = 0;k < 10;k++)
1049 altanims[k] = anims[k];
1052 // link together the primary animation
1053 for (j = 0;j < max;j++)
1056 tx2->animated = true;
1057 tx2->anim_total[0] = max;
1058 tx2->anim_total[1] = altmax;
1059 for (k = 0;k < 10;k++)
1061 tx2->anim_frames[0][k] = anims[k];
1062 tx2->anim_frames[1][k] = altanims[k];
1066 // if there really is an alternate anim...
1067 if (anims[0] != altanims[0])
1069 // link together the alternate animation
1070 for (j = 0;j < altmax;j++)
1073 tx2->animated = true;
1074 // the primary/alternate are reversed here
1075 tx2->anim_total[0] = altmax;
1076 tx2->anim_total[1] = max;
1077 for (k = 0;k < 10;k++)
1079 tx2->anim_frames[0][k] = altanims[k];
1080 tx2->anim_frames[1][k] = anims[k];
1087 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1090 qbyte *in, *out, *data, d;
1091 char litfilename[1024];
1092 loadmodel->brushq1.lightdata = NULL;
1093 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1095 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1096 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1098 else // LordHavoc: bsp version 29 (normal white lighting)
1100 // LordHavoc: hope is not lost yet, check for a .lit file to load
1101 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1102 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1103 strlcat (litfilename, ".lit", sizeof (litfilename));
1104 data = (qbyte*) FS_LoadFile(litfilename, false);
1107 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1109 i = LittleLong(((int *)data)[1]);
1112 Con_DPrintf("loaded %s\n", litfilename);
1113 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1114 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1120 Con_Printf("Unknown .lit file version (%d)\n", i);
1126 if (fs_filesize == 8)
1127 Con_Printf("Empty .lit file, ignoring\n");
1129 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1133 // LordHavoc: oh well, expand the white lighting data
1136 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1137 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1138 out = loadmodel->brushq1.lightdata;
1139 memcpy(in, mod_base + l->fileofs, l->filelen);
1140 for (i = 0;i < l->filelen;i++)
1150 static void Mod_Q1BSP_LoadLightList(void)
1152 int a, n, numlights;
1153 char lightsfilename[1024], *s, *t, *lightsstring;
1156 strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1157 FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1158 strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1159 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1165 while (*s && *s != '\n')
1169 Mem_Free(lightsstring);
1170 Host_Error("lights file must end with a newline\n");
1175 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1178 while (*s && n < numlights)
1181 while (*s && *s != '\n')
1185 Mem_Free(lightsstring);
1186 Host_Error("misparsed lights file!\n");
1188 e = loadmodel->brushq1.lights + n;
1190 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);
1194 Mem_Free(lightsstring);
1195 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);
1202 Mem_Free(lightsstring);
1203 Host_Error("misparsed lights file!\n");
1205 loadmodel->brushq1.numlights = numlights;
1206 Mem_Free(lightsstring);
1210 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1212 loadmodel->brushq1.num_compressedpvs = 0;
1213 loadmodel->brushq1.data_compressedpvs = NULL;
1216 loadmodel->brushq1.num_compressedpvs = l->filelen;
1217 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1218 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1221 // used only for HalfLife maps
1222 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1224 char key[128], value[4096];
1229 if (!COM_ParseToken(&data, false))
1231 if (com_token[0] != '{')
1235 if (!COM_ParseToken(&data, false))
1237 if (com_token[0] == '}')
1238 break; // end of worldspawn
1239 if (com_token[0] == '_')
1240 strcpy(key, com_token + 1);
1242 strcpy(key, com_token);
1243 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1244 key[strlen(key)-1] = 0;
1245 if (!COM_ParseToken(&data, false))
1247 strcpy(value, com_token);
1248 if (!strcmp("wad", key)) // for HalfLife maps
1250 if (loadmodel->brush.ishlbsp)
1253 for (i = 0;i < 4096;i++)
1254 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1260 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1261 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1263 else if (value[i] == ';' || value[i] == 0)
1267 strcpy(wadname, "textures/");
1268 strcat(wadname, &value[j]);
1269 W_LoadTextureWadFile(wadname, false);
1281 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1283 loadmodel->brush.entities = NULL;
1286 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1287 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1288 if (loadmodel->brush.ishlbsp)
1289 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1293 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1299 in = (void *)(mod_base + l->fileofs);
1300 if (l->filelen % sizeof(*in))
1301 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1302 count = l->filelen / sizeof(*in);
1303 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1305 loadmodel->brushq1.vertexes = out;
1306 loadmodel->brushq1.numvertexes = count;
1308 for ( i=0 ; i<count ; i++, in++, out++)
1310 out->position[0] = LittleFloat(in->point[0]);
1311 out->position[1] = LittleFloat(in->point[1]);
1312 out->position[2] = LittleFloat(in->point[2]);
1316 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1322 in = (void *)(mod_base + l->fileofs);
1323 if (l->filelen % sizeof(*in))
1324 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1325 count = l->filelen / sizeof(*in);
1326 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1328 loadmodel->brushq1.submodels = out;
1329 loadmodel->brush.numsubmodels = count;
1331 for ( i=0 ; i<count ; i++, in++, out++)
1333 for (j=0 ; j<3 ; j++)
1335 // spread the mins / maxs by a pixel
1336 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1337 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1338 out->origin[j] = LittleFloat(in->origin[j]);
1340 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1341 out->headnode[j] = LittleLong(in->headnode[j]);
1342 out->visleafs = LittleLong(in->visleafs);
1343 out->firstface = LittleLong(in->firstface);
1344 out->numfaces = LittleLong(in->numfaces);
1348 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1354 in = (void *)(mod_base + l->fileofs);
1355 if (l->filelen % sizeof(*in))
1356 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1357 count = l->filelen / sizeof(*in);
1358 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1360 loadmodel->brushq1.edges = out;
1361 loadmodel->brushq1.numedges = count;
1363 for ( i=0 ; i<count ; i++, in++, out++)
1365 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1366 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1370 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1374 int i, j, k, count, miptex;
1376 in = (void *)(mod_base + l->fileofs);
1377 if (l->filelen % sizeof(*in))
1378 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1379 count = l->filelen / sizeof(*in);
1380 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1382 loadmodel->brushq1.texinfo = out;
1383 loadmodel->brushq1.numtexinfo = count;
1385 for (i = 0;i < count;i++, in++, out++)
1387 for (k = 0;k < 2;k++)
1388 for (j = 0;j < 4;j++)
1389 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1391 miptex = LittleLong(in->miptex);
1392 out->flags = LittleLong(in->flags);
1394 out->texture = NULL;
1395 if (loadmodel->brushq1.textures)
1397 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1398 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1400 out->texture = loadmodel->brushq1.textures + miptex;
1402 if (out->flags & TEX_SPECIAL)
1404 // if texture chosen is NULL or the shader needs a lightmap,
1405 // force to notexture water shader
1406 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1407 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1411 // if texture chosen is NULL, force to notexture
1412 if (out->texture == NULL)
1413 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1419 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1424 mins[0] = mins[1] = mins[2] = 9999;
1425 maxs[0] = maxs[1] = maxs[2] = -9999;
1427 for (i = 0;i < numverts;i++)
1429 for (j = 0;j < 3;j++, v++)
1439 #define MAX_SUBDIVPOLYTRIANGLES 4096
1440 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1442 static int subdivpolyverts, subdivpolytriangles;
1443 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1444 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1446 static int subdivpolylookupvert(vec3_t v)
1449 for (i = 0;i < subdivpolyverts;i++)
1450 if (subdivpolyvert[i][0] == v[0]
1451 && subdivpolyvert[i][1] == v[1]
1452 && subdivpolyvert[i][2] == v[2])
1454 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1455 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1456 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1457 return subdivpolyverts++;
1460 static void SubdividePolygon(int numverts, float *verts)
1462 int i, i1, i2, i3, f, b, c, p;
1463 vec3_t mins, maxs, front[256], back[256];
1464 float m, *pv, *cv, dist[256], frac;
1467 Host_Error("SubdividePolygon: ran out of verts in buffer");
1469 BoundPoly(numverts, verts, mins, maxs);
1471 for (i = 0;i < 3;i++)
1473 m = (mins[i] + maxs[i]) * 0.5;
1474 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1475 if (maxs[i] - m < 8)
1477 if (m - mins[i] < 8)
1481 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1482 dist[c] = cv[i] - m;
1485 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1489 VectorCopy(pv, front[f]);
1494 VectorCopy(pv, back[b]);
1497 if (dist[p] == 0 || dist[c] == 0)
1499 if ((dist[p] > 0) != (dist[c] > 0) )
1502 frac = dist[p] / (dist[p] - dist[c]);
1503 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1504 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1505 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1511 SubdividePolygon(f, front[0]);
1512 SubdividePolygon(b, back[0]);
1516 i1 = subdivpolylookupvert(verts);
1517 i2 = subdivpolylookupvert(verts + 3);
1518 for (i = 2;i < numverts;i++)
1520 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1522 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1526 i3 = subdivpolylookupvert(verts + i * 3);
1527 subdivpolyindex[subdivpolytriangles][0] = i1;
1528 subdivpolyindex[subdivpolytriangles][1] = i2;
1529 subdivpolyindex[subdivpolytriangles][2] = i3;
1531 subdivpolytriangles++;
1535 //Breaks a polygon up along axial 64 unit
1536 //boundaries so that turbulent and sky warps
1537 //can be done reasonably.
1538 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1544 subdivpolytriangles = 0;
1545 subdivpolyverts = 0;
1546 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1547 if (subdivpolytriangles < 1)
1548 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1550 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1551 mesh->num_vertices = subdivpolyverts;
1552 mesh->num_triangles = subdivpolytriangles;
1553 mesh->vertex = (surfvertex_t *)(mesh + 1);
1554 mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1555 memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1557 for (i = 0;i < mesh->num_triangles;i++)
1558 for (j = 0;j < 3;j++)
1559 mesh->index[i*3+j] = subdivpolyindex[i][j];
1561 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1563 VectorCopy(subdivpolyvert[i], v->v);
1564 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1565 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1570 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1573 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1574 mesh->num_vertices = numverts;
1575 mesh->num_triangles = numtriangles;
1576 mesh->data_vertex3f = (float *)(mesh + 1);
1577 mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1578 mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1579 mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1580 mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1581 mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1582 mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1583 mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1584 mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1585 mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1589 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1592 float *vec, *vert, mins[3], maxs[3], val, *v;
1595 // convert edges back to a normal polygon
1596 surf->poly_numverts = numedges;
1597 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1598 for (i = 0;i < numedges;i++)
1600 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1602 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1604 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1605 VectorCopy(vec, vert);
1609 // calculate polygon bounding box and center
1610 vert = surf->poly_verts;
1611 VectorCopy(vert, mins);
1612 VectorCopy(vert, maxs);
1614 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1616 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1617 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1618 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1620 VectorCopy(mins, surf->poly_mins);
1621 VectorCopy(maxs, surf->poly_maxs);
1622 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1623 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1624 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1626 // generate surface extents information
1627 tex = surf->texinfo;
1628 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1629 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1630 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1632 for (j = 0;j < 2;j++)
1634 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1641 for (i = 0;i < 2;i++)
1643 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1644 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1648 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1652 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1656 in = (void *)(mod_base + l->fileofs);
1657 if (l->filelen % sizeof(*in))
1658 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1659 count = l->filelen / sizeof(*in);
1660 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1662 loadmodel->brushq1.numsurfaces = count;
1663 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1664 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1665 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1667 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++)
1669 surf->number = surfnum;
1670 // FIXME: validate edges, texinfo, etc?
1671 firstedge = LittleLong(in->firstedge);
1672 numedges = LittleShort(in->numedges);
1673 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)
1674 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1675 i = LittleShort(in->texinfo);
1676 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1677 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1678 surf->texinfo = loadmodel->brushq1.texinfo + i;
1679 surf->flags = surf->texinfo->texture->flags;
1681 planenum = LittleShort(in->planenum);
1682 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1683 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1685 if (LittleShort(in->side))
1686 surf->flags |= SURF_PLANEBACK;
1688 surf->plane = loadmodel->brushq1.planes + planenum;
1690 // clear lightmap (filled in later)
1691 surf->lightmaptexture = NULL;
1693 // force lightmap upload on first time seeing the surface
1694 surf->cached_dlight = true;
1696 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1698 ssize = (surf->extents[0] >> 4) + 1;
1699 tsize = (surf->extents[1] >> 4) + 1;
1702 for (i = 0;i < MAXLIGHTMAPS;i++)
1703 surf->styles[i] = in->styles[i];
1704 i = LittleLong(in->lightofs);
1706 surf->samples = NULL;
1707 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1708 surf->samples = loadmodel->brushq1.lightdata + i;
1709 else // LordHavoc: white lighting (bsp version 29)
1710 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1712 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1714 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1715 Host_Error("Bad surface extents");
1716 // stainmap for permanent marks on walls
1717 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1719 memset(surf->stainsamples, 255, ssize * tsize * 3);
1723 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1725 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++)
1728 mesh->num_vertices = surf->poly_numverts;
1729 mesh->num_triangles = surf->poly_numverts - 2;
1730 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1731 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1732 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1733 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1734 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1735 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1736 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1737 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1738 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1739 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1741 surf->lightmaptexturestride = 0;
1742 surf->lightmaptexture = NULL;
1744 for (i = 0;i < mesh->num_vertices;i++)
1746 mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1747 mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1748 mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1749 s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1750 t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1751 mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1752 mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1753 mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1754 mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1755 mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1756 mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1757 mesh->data_lightmapoffsets[i] = 0;
1760 for (i = 0;i < mesh->num_triangles;i++)
1762 mesh->data_element3i[i * 3 + 0] = 0;
1763 mesh->data_element3i[i * 3 + 1] = i + 1;
1764 mesh->data_element3i[i * 3 + 2] = i + 2;
1767 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1768 Mod_BuildTextureVectorsAndNormals(mesh->num_vertices, mesh->num_triangles, mesh->data_vertex3f, mesh->data_texcoordtexture2f, mesh->data_element3i, mesh->data_svector3f, mesh->data_tvector3f, mesh->data_normal3f);
1770 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1772 int i, iu, iv, smax, tmax;
1773 float u, v, ubase, vbase, uscale, vscale;
1775 smax = surf->extents[0] >> 4;
1776 tmax = surf->extents[1] >> 4;
1778 surf->flags |= SURF_LIGHTMAP;
1779 if (r_miplightmaps.integer)
1781 surf->lightmaptexturestride = smax+1;
1782 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);
1786 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1787 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);
1789 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1790 uscale = (uscale - ubase) / (smax + 1);
1791 vscale = (vscale - vbase) / (tmax + 1);
1793 for (i = 0;i < mesh->num_vertices;i++)
1795 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1796 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1797 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1798 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1799 // LordHavoc: calc lightmap data offset for vertex lighting to use
1802 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1808 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1810 node->parent = parent;
1811 if (node->contents < 0)
1813 Mod_Q1BSP_SetParent(node->children[0], node);
1814 Mod_Q1BSP_SetParent(node->children[1], node);
1817 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1823 in = (void *)(mod_base + l->fileofs);
1824 if (l->filelen % sizeof(*in))
1825 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1826 count = l->filelen / sizeof(*in);
1827 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1829 loadmodel->brushq1.nodes = out;
1830 loadmodel->brushq1.numnodes = count;
1832 for ( i=0 ; i<count ; i++, in++, out++)
1834 for (j=0 ; j<3 ; j++)
1836 out->mins[j] = LittleShort(in->mins[j]);
1837 out->maxs[j] = LittleShort(in->maxs[j]);
1840 p = LittleLong(in->planenum);
1841 out->plane = loadmodel->brushq1.planes + p;
1843 out->firstsurface = LittleShort(in->firstface);
1844 out->numsurfaces = LittleShort(in->numfaces);
1846 for (j=0 ; j<2 ; j++)
1848 p = LittleShort(in->children[j]);
1850 out->children[j] = loadmodel->brushq1.nodes + p;
1852 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1856 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1859 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1863 int i, j, count, p, pvschainbytes;
1866 in = (void *)(mod_base + l->fileofs);
1867 if (l->filelen % sizeof(*in))
1868 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1869 count = l->filelen / sizeof(*in);
1870 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1872 loadmodel->brushq1.leafs = out;
1873 loadmodel->brushq1.numleafs = count;
1874 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1875 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1877 for ( i=0 ; i<count ; i++, in++, out++)
1879 for (j=0 ; j<3 ; j++)
1881 out->mins[j] = LittleShort(in->mins[j]);
1882 out->maxs[j] = LittleShort(in->maxs[j]);
1885 // FIXME: this function could really benefit from some error checking
1887 out->contents = LittleLong(in->contents);
1889 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1890 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1891 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
1893 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);
1894 out->firstmarksurface = NULL;
1895 out->nummarksurfaces = 0;
1899 memset(out->pvsdata, 0xFF, pvschainbytes);
1900 pvs += pvschainbytes;
1902 p = LittleLong(in->visofs);
1905 if (p >= loadmodel->brushq1.num_compressedpvs)
1906 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
1908 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1911 for (j = 0;j < 4;j++)
1912 out->ambient_sound_level[j] = in->ambient_level[j];
1914 // FIXME: Insert caustics here
1918 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1920 dclipnode_t *in, *out;
1924 in = (void *)(mod_base + l->fileofs);
1925 if (l->filelen % sizeof(*in))
1926 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1927 count = l->filelen / sizeof(*in);
1928 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1930 loadmodel->brushq1.clipnodes = out;
1931 loadmodel->brushq1.numclipnodes = count;
1933 if (loadmodel->brush.ishlbsp)
1935 hull = &loadmodel->brushq1.hulls[1];
1936 hull->clipnodes = out;
1937 hull->firstclipnode = 0;
1938 hull->lastclipnode = count-1;
1939 hull->planes = loadmodel->brushq1.planes;
1940 hull->clip_mins[0] = -16;
1941 hull->clip_mins[1] = -16;
1942 hull->clip_mins[2] = -36;
1943 hull->clip_maxs[0] = 16;
1944 hull->clip_maxs[1] = 16;
1945 hull->clip_maxs[2] = 36;
1946 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1948 hull = &loadmodel->brushq1.hulls[2];
1949 hull->clipnodes = out;
1950 hull->firstclipnode = 0;
1951 hull->lastclipnode = count-1;
1952 hull->planes = loadmodel->brushq1.planes;
1953 hull->clip_mins[0] = -32;
1954 hull->clip_mins[1] = -32;
1955 hull->clip_mins[2] = -32;
1956 hull->clip_maxs[0] = 32;
1957 hull->clip_maxs[1] = 32;
1958 hull->clip_maxs[2] = 32;
1959 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1961 hull = &loadmodel->brushq1.hulls[3];
1962 hull->clipnodes = out;
1963 hull->firstclipnode = 0;
1964 hull->lastclipnode = count-1;
1965 hull->planes = loadmodel->brushq1.planes;
1966 hull->clip_mins[0] = -16;
1967 hull->clip_mins[1] = -16;
1968 hull->clip_mins[2] = -18;
1969 hull->clip_maxs[0] = 16;
1970 hull->clip_maxs[1] = 16;
1971 hull->clip_maxs[2] = 18;
1972 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1976 hull = &loadmodel->brushq1.hulls[1];
1977 hull->clipnodes = out;
1978 hull->firstclipnode = 0;
1979 hull->lastclipnode = count-1;
1980 hull->planes = loadmodel->brushq1.planes;
1981 hull->clip_mins[0] = -16;
1982 hull->clip_mins[1] = -16;
1983 hull->clip_mins[2] = -24;
1984 hull->clip_maxs[0] = 16;
1985 hull->clip_maxs[1] = 16;
1986 hull->clip_maxs[2] = 32;
1987 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1989 hull = &loadmodel->brushq1.hulls[2];
1990 hull->clipnodes = out;
1991 hull->firstclipnode = 0;
1992 hull->lastclipnode = count-1;
1993 hull->planes = loadmodel->brushq1.planes;
1994 hull->clip_mins[0] = -32;
1995 hull->clip_mins[1] = -32;
1996 hull->clip_mins[2] = -24;
1997 hull->clip_maxs[0] = 32;
1998 hull->clip_maxs[1] = 32;
1999 hull->clip_maxs[2] = 64;
2000 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2003 for (i=0 ; i<count ; i++, out++, in++)
2005 out->planenum = LittleLong(in->planenum);
2006 out->children[0] = LittleShort(in->children[0]);
2007 out->children[1] = LittleShort(in->children[1]);
2008 if (out->children[0] >= count || out->children[1] >= count)
2009 Host_Error("Corrupt clipping hull(out of range child)\n");
2013 //Duplicate the drawing hull structure as a clipping hull
2014 static void Mod_Q1BSP_MakeHull0(void)
2021 hull = &loadmodel->brushq1.hulls[0];
2023 in = loadmodel->brushq1.nodes;
2024 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
2026 hull->clipnodes = out;
2027 hull->firstclipnode = 0;
2028 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
2029 hull->planes = loadmodel->brushq1.planes;
2031 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
2033 out->planenum = in->plane - loadmodel->brushq1.planes;
2034 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
2035 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
2039 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
2044 in = (void *)(mod_base + l->fileofs);
2045 if (l->filelen % sizeof(*in))
2046 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2047 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2048 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2050 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2052 j = (unsigned) LittleShort(in[i]);
2053 if (j >= loadmodel->brushq1.numsurfaces)
2054 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2055 loadmodel->brushq1.marksurfaces[i] = j;
2059 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2064 in = (void *)(mod_base + l->fileofs);
2065 if (l->filelen % sizeof(*in))
2066 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2067 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2068 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2070 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2071 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2075 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2081 in = (void *)(mod_base + l->fileofs);
2082 if (l->filelen % sizeof(*in))
2083 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2085 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2086 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2088 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2090 out->normal[0] = LittleFloat(in->normal[0]);
2091 out->normal[1] = LittleFloat(in->normal[1]);
2092 out->normal[2] = LittleFloat(in->normal[2]);
2093 out->dist = LittleFloat(in->dist);
2099 typedef struct portal_s
2102 mnode_t *nodes[2]; // [0] = front side of plane
2103 struct portal_s *next[2];
2105 struct portal_s *chain; // all portals are linked into a list
2109 static portal_t *portalchain;
2116 static portal_t *AllocPortal(void)
2119 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2120 p->chain = portalchain;
2125 static void FreePortal(portal_t *p)
2130 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2132 // calculate children first
2133 if (node->children[0]->contents >= 0)
2134 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2135 if (node->children[1]->contents >= 0)
2136 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2138 // make combined bounding box from children
2139 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2140 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2141 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2142 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2143 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2144 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2147 static void Mod_Q1BSP_FinalizePortals(void)
2149 int i, j, numportals, numpoints;
2150 portal_t *p, *pnext;
2153 mleaf_t *leaf, *endleaf;
2156 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2157 leaf = loadmodel->brushq1.leafs;
2158 endleaf = leaf + loadmodel->brushq1.numleafs;
2159 for (;leaf < endleaf;leaf++)
2161 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2162 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2169 for (i = 0;i < 2;i++)
2171 leaf = (mleaf_t *)p->nodes[i];
2173 for (j = 0;j < w->numpoints;j++)
2175 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2176 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2177 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2178 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2179 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2180 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2187 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2189 // tally up portal and point counts
2195 // note: this check must match the one below or it will usually corrupt memory
2196 // 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
2197 if (p->winding && p->nodes[0] != p->nodes[1]
2198 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2199 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2202 numpoints += p->winding->numpoints * 2;
2206 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2207 loadmodel->brushq1.numportals = numportals;
2208 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2209 loadmodel->brushq1.numportalpoints = numpoints;
2210 // clear all leaf portal chains
2211 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2212 loadmodel->brushq1.leafs[i].portals = NULL;
2213 // process all portals in the global portal chain, while freeing them
2214 portal = loadmodel->brushq1.portals;
2215 point = loadmodel->brushq1.portalpoints;
2224 // note: this check must match the one above or it will usually corrupt memory
2225 // 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
2226 if (p->nodes[0] != p->nodes[1]
2227 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2228 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2230 // first make the back to front portal(forward portal)
2231 portal->points = point;
2232 portal->numpoints = p->winding->numpoints;
2233 portal->plane.dist = p->plane.dist;
2234 VectorCopy(p->plane.normal, portal->plane.normal);
2235 portal->here = (mleaf_t *)p->nodes[1];
2236 portal->past = (mleaf_t *)p->nodes[0];
2238 for (j = 0;j < portal->numpoints;j++)
2240 VectorCopy(p->winding->points[j], point->position);
2243 PlaneClassify(&portal->plane);
2245 // link into leaf's portal chain
2246 portal->next = portal->here->portals;
2247 portal->here->portals = portal;
2249 // advance to next portal
2252 // then make the front to back portal(backward portal)
2253 portal->points = point;
2254 portal->numpoints = p->winding->numpoints;
2255 portal->plane.dist = -p->plane.dist;
2256 VectorNegate(p->plane.normal, portal->plane.normal);
2257 portal->here = (mleaf_t *)p->nodes[0];
2258 portal->past = (mleaf_t *)p->nodes[1];
2260 for (j = portal->numpoints - 1;j >= 0;j--)
2262 VectorCopy(p->winding->points[j], point->position);
2265 PlaneClassify(&portal->plane);
2267 // link into leaf's portal chain
2268 portal->next = portal->here->portals;
2269 portal->here->portals = portal;
2271 // advance to next portal
2274 Winding_Free(p->winding);
2286 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2289 Host_Error("AddPortalToNodes: NULL front node");
2291 Host_Error("AddPortalToNodes: NULL back node");
2292 if (p->nodes[0] || p->nodes[1])
2293 Host_Error("AddPortalToNodes: already included");
2294 // 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
2296 p->nodes[0] = front;
2297 p->next[0] = (portal_t *)front->portals;
2298 front->portals = (mportal_t *)p;
2301 p->next[1] = (portal_t *)back->portals;
2302 back->portals = (mportal_t *)p;
2307 RemovePortalFromNode
2310 static void RemovePortalFromNodes(portal_t *portal)
2314 void **portalpointer;
2316 for (i = 0;i < 2;i++)
2318 node = portal->nodes[i];
2320 portalpointer = (void **) &node->portals;
2325 Host_Error("RemovePortalFromNodes: portal not in leaf");
2329 if (portal->nodes[0] == node)
2331 *portalpointer = portal->next[0];
2332 portal->nodes[0] = NULL;
2334 else if (portal->nodes[1] == node)
2336 *portalpointer = portal->next[1];
2337 portal->nodes[1] = NULL;
2340 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2344 if (t->nodes[0] == node)
2345 portalpointer = (void **) &t->next[0];
2346 else if (t->nodes[1] == node)
2347 portalpointer = (void **) &t->next[1];
2349 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2354 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2357 mnode_t *front, *back, *other_node;
2358 mplane_t clipplane, *plane;
2359 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2360 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2362 // if a leaf, we're done
2366 plane = node->plane;
2368 front = node->children[0];
2369 back = node->children[1];
2371 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2373 // create the new portal by generating a polygon for the node plane,
2374 // and clipping it by all of the other portals(which came from nodes above this one)
2375 nodeportal = AllocPortal();
2376 nodeportal->plane = *plane;
2378 nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2379 side = 0; // shut up compiler warning
2380 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2382 clipplane = portal->plane;
2383 if (portal->nodes[0] == portal->nodes[1])
2384 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2385 if (portal->nodes[0] == node)
2387 else if (portal->nodes[1] == node)
2389 clipplane.dist = -clipplane.dist;
2390 VectorNegate(clipplane.normal, clipplane.normal);
2394 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2396 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2397 if (!nodeportalwinding)
2399 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2404 if (nodeportalwinding)
2406 // if the plane was not clipped on all sides, there was an error
2407 nodeportal->winding = nodeportalwinding;
2408 AddPortalToNodes(nodeportal, front, back);
2411 // split the portals of this node along this node's plane and assign them to the children of this node
2412 // (migrating the portals downward through the tree)
2413 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2415 if (portal->nodes[0] == portal->nodes[1])
2416 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2417 if (portal->nodes[0] == node)
2419 else if (portal->nodes[1] == node)
2422 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2423 nextportal = portal->next[side];
2425 other_node = portal->nodes[!side];
2426 RemovePortalFromNodes(portal);
2428 // cut the portal into two portals, one on each side of the node plane
2429 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2434 AddPortalToNodes(portal, back, other_node);
2436 AddPortalToNodes(portal, other_node, back);
2442 AddPortalToNodes(portal, front, other_node);
2444 AddPortalToNodes(portal, other_node, front);
2448 // the winding is split
2449 splitportal = AllocPortal();
2450 temp = splitportal->chain;
2451 *splitportal = *portal;
2452 splitportal->chain = temp;
2453 splitportal->winding = backwinding;
2454 Winding_Free(portal->winding);
2455 portal->winding = frontwinding;
2459 AddPortalToNodes(portal, front, other_node);
2460 AddPortalToNodes(splitportal, back, other_node);
2464 AddPortalToNodes(portal, other_node, front);
2465 AddPortalToNodes(splitportal, other_node, back);
2469 Mod_Q1BSP_RecursiveNodePortals(front);
2470 Mod_Q1BSP_RecursiveNodePortals(back);
2473 static void Mod_Q1BSP_MakePortals(void)
2476 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2477 Mod_Q1BSP_FinalizePortals();
2480 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2483 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2484 msurface_t *surf, *s;
2485 float *v0, *v1, *v2, *v3;
2486 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2487 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2488 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2490 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)
2492 if (surf->neighborsurfaces[vertnum])
2494 surf->neighborsurfaces[vertnum] = NULL;
2495 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2497 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2498 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2499 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2502 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2503 if (s->neighborsurfaces[vnum] == surf)
2505 if (vnum < s->poly_numverts)
2507 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)
2509 if (s->neighborsurfaces[vnum] == NULL
2510 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2511 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2513 surf->neighborsurfaces[vertnum] = s;
2514 s->neighborsurfaces[vnum] = surf;
2518 if (vnum < s->poly_numverts)
2526 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2528 int i, j, stylecounts[256], totalcount, remapstyles[256];
2530 memset(stylecounts, 0, sizeof(stylecounts));
2531 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2533 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2534 for (j = 0;j < MAXLIGHTMAPS;j++)
2535 stylecounts[surf->styles[j]]++;
2538 model->brushq1.light_styles = 0;
2539 for (i = 0;i < 255;i++)
2543 remapstyles[i] = model->brushq1.light_styles++;
2544 totalcount += stylecounts[i] + 1;
2549 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2550 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2551 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2552 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2553 model->brushq1.light_styles = 0;
2554 for (i = 0;i < 255;i++)
2556 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2558 for (i = 0;i < model->brushq1.light_styles;i++)
2560 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2561 j += stylecounts[model->brushq1.light_style[i]] + 1;
2563 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2565 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2566 for (j = 0;j < MAXLIGHTMAPS;j++)
2567 if (surf->styles[j] != 255)
2568 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2571 for (i = 0;i < model->brushq1.light_styles;i++)
2573 *model->brushq1.light_styleupdatechains[i] = NULL;
2574 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2575 j += stylecounts[model->brushq1.light_style[i]] + 1;
2579 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2582 for (i = 0;i < model->brushq1.numtextures;i++)
2583 model->brushq1.pvstexturechainslength[i] = 0;
2584 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2586 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2588 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2589 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2592 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2594 if (model->brushq1.pvstexturechainslength[i])
2596 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2597 j += model->brushq1.pvstexturechainslength[i] + 1;
2600 model->brushq1.pvstexturechains[i] = NULL;
2602 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2603 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2604 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2605 for (i = 0;i < model->brushq1.numtextures;i++)
2607 if (model->brushq1.pvstexturechainslength[i])
2609 *model->brushq1.pvstexturechains[i] = NULL;
2610 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2615 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2620 while (node->contents >= 0)
2622 d = PlaneDiff(org, node->plane);
2624 node = node->children[0];
2625 else if (d < -radius)
2626 node = node->children[1];
2629 // go down both sides
2630 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2631 node = node->children[1];
2635 // if this is a leaf, accumulate the pvs bits
2636 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2637 for (i = 0;i < pvsbytes;i++)
2638 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2641 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2642 //of the given point.
2643 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2645 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2646 bytes = min(bytes, pvsbufferlength);
2647 memset(pvsbuffer, 0, bytes);
2648 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2652 //Returns PVS data for a given point
2653 //(note: always returns valid data, never NULL)
2654 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2657 Mod_CheckLoaded(model);
2658 // LordHavoc: modified to start at first clip node,
2659 // in other words: first node of the (sub)model
2660 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2661 while (node->contents == 0)
2662 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2663 return ((mleaf_t *)node)->pvsdata;
2666 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2671 VectorSubtract(inmaxs, inmins, size);
2672 if (cmodel->brush.ishlbsp)
2675 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2676 else if (size[0] <= 32)
2678 if (size[2] < 54) // pick the nearest of 36 or 72
2679 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2681 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2684 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2689 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2690 else if (size[0] <= 32)
2691 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2693 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2695 VectorCopy(inmins, outmins);
2696 VectorAdd(inmins, hull->clip_size, outmaxs);
2699 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2700 extern void R_Model_Brush_Draw(entity_render_t *ent);
2701 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2702 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);
2703 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2708 mempool_t *mainmempool;
2710 model_t *originalloadmodel;
2711 float dist, modelyawradius, modelradius, *vec;
2714 mod->type = mod_brush;
2716 header = (dheader_t *)buffer;
2718 i = LittleLong(header->version);
2719 if (i != BSPVERSION && i != 30)
2720 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2721 mod->brush.ishlbsp = i == 30;
2723 mod->soundfromcenter = true;
2724 mod->TraceBox = Mod_Q1BSP_TraceBox;
2725 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2726 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2727 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2728 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2729 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2730 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2731 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2732 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2733 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2734 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2735 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2737 if (loadmodel->isworldmodel)
2739 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2740 // until we get a texture for it...
2744 // swap all the lumps
2745 mod_base = (qbyte *)header;
2747 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2748 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2752 // store which lightmap format to use
2753 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2755 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2756 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2757 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2758 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2759 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2760 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2761 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2762 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2763 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2764 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2765 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2766 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2767 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2768 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2769 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2771 if (mod->brushq1.data_compressedpvs)
2772 Mem_Free(mod->brushq1.data_compressedpvs);
2773 mod->brushq1.data_compressedpvs = NULL;
2774 mod->brushq1.num_compressedpvs = 0;
2776 Mod_Q1BSP_MakeHull0();
2777 Mod_Q1BSP_MakePortals();
2779 if (developer.integer)
2780 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);
2782 mod->numframes = 2; // regular and alternate animation
2784 mainmempool = mod->mempool;
2785 loadname = mod->name;
2787 Mod_Q1BSP_LoadLightList();
2788 originalloadmodel = loadmodel;
2791 // set up the submodels(FIXME: this is confusing)
2793 for (i = 0;i < mod->brush.numsubmodels;i++)
2795 bm = &mod->brushq1.submodels[i];
2797 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2798 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2800 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2801 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2804 mod->brushq1.firstmodelsurface = bm->firstface;
2805 mod->brushq1.nummodelsurfaces = bm->numfaces;
2807 // this gets altered below if sky is used
2808 mod->DrawSky = NULL;
2809 mod->Draw = R_Model_Brush_Draw;
2810 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2811 mod->DrawLight = R_Model_Brush_DrawLight;
2812 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2813 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2814 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2815 Mod_Q1BSP_BuildPVSTextureChains(mod);
2816 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2817 if (mod->brushq1.nummodelsurfaces)
2819 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2820 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2821 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2824 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2826 // we only need to have a drawsky function if it is used(usually only on world model)
2827 if (surf->texinfo->texture->shader == &Cshader_sky)
2828 mod->DrawSky = R_Model_Brush_DrawSky;
2829 // LordHavoc: submodels always clip, even if water
2830 if (mod->brush.numsubmodels - 1)
2831 surf->flags |= SURF_SOLIDCLIP;
2832 // calculate bounding shapes
2833 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
2835 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2836 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2837 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2838 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2839 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2840 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2841 dist = vec[0]*vec[0]+vec[1]*vec[1];
2842 if (modelyawradius < dist)
2843 modelyawradius = dist;
2844 dist += vec[2]*vec[2];
2845 if (modelradius < dist)
2849 modelyawradius = sqrt(modelyawradius);
2850 modelradius = sqrt(modelradius);
2851 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2852 mod->yawmins[2] = mod->normalmins[2];
2853 mod->yawmaxs[2] = mod->normalmaxs[2];
2854 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2855 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2856 mod->radius = modelradius;
2857 mod->radius2 = modelradius * modelradius;
2861 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2862 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2864 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2866 mod->brushq1.visleafs = bm->visleafs;
2868 // LordHavoc: only register submodels if it is the world
2869 // (prevents bsp models from replacing world submodels)
2870 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2873 // duplicate the basic information
2874 sprintf(name, "*%i", i+1);
2875 loadmodel = Mod_FindName(name);
2877 strcpy(loadmodel->name, name);
2878 // textures and memory belong to the main model
2879 loadmodel->texturepool = NULL;
2880 loadmodel->mempool = NULL;
2885 loadmodel = originalloadmodel;
2886 //Mod_Q1BSP_ProcessLightList();
2889 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2893 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2900 in = (void *)(mod_base + l->fileofs);
2901 if (l->filelen % sizeof(*in))
2902 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2903 count = l->filelen / sizeof(*in);
2904 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2907 loadmodel->num = count;
2909 for (i = 0;i < count;i++, in++, out++)
2915 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2922 in = (void *)(mod_base + l->fileofs);
2923 if (l->filelen % sizeof(*in))
2924 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2925 count = l->filelen / sizeof(*in);
2926 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2929 loadmodel->num = count;
2931 for (i = 0;i < count;i++, in++, out++)
2937 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2944 in = (void *)(mod_base + l->fileofs);
2945 if (l->filelen % sizeof(*in))
2946 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2947 count = l->filelen / sizeof(*in);
2948 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2951 loadmodel->num = count;
2953 for (i = 0;i < count;i++, in++, out++)
2959 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2966 in = (void *)(mod_base + l->fileofs);
2967 if (l->filelen % sizeof(*in))
2968 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2969 count = l->filelen / sizeof(*in);
2970 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2973 loadmodel->num = count;
2975 for (i = 0;i < count;i++, in++, out++)
2981 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2988 in = (void *)(mod_base + l->fileofs);
2989 if (l->filelen % sizeof(*in))
2990 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2991 count = l->filelen / sizeof(*in);
2992 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2995 loadmodel->num = count;
2997 for (i = 0;i < count;i++, in++, out++)
3003 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3010 in = (void *)(mod_base + l->fileofs);
3011 if (l->filelen % sizeof(*in))
3012 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3013 count = l->filelen / sizeof(*in);
3014 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3017 loadmodel->num = count;
3019 for (i = 0;i < count;i++, in++, out++)
3025 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3032 in = (void *)(mod_base + l->fileofs);
3033 if (l->filelen % sizeof(*in))
3034 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3035 count = l->filelen / sizeof(*in);
3036 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3039 loadmodel->num = count;
3041 for (i = 0;i < count;i++, in++, out++)
3047 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3054 in = (void *)(mod_base + l->fileofs);
3055 if (l->filelen % sizeof(*in))
3056 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3057 count = l->filelen / sizeof(*in);
3058 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3061 loadmodel->num = count;
3063 for (i = 0;i < count;i++, in++, out++)
3069 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3076 in = (void *)(mod_base + l->fileofs);
3077 if (l->filelen % sizeof(*in))
3078 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3079 count = l->filelen / sizeof(*in);
3080 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3083 loadmodel->num = count;
3085 for (i = 0;i < count;i++, in++, out++)
3091 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3098 in = (void *)(mod_base + l->fileofs);
3099 if (l->filelen % sizeof(*in))
3100 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3101 count = l->filelen / sizeof(*in);
3102 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3105 loadmodel->num = count;
3107 for (i = 0;i < count;i++, in++, out++)
3113 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3120 in = (void *)(mod_base + l->fileofs);
3121 if (l->filelen % sizeof(*in))
3122 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3123 count = l->filelen / sizeof(*in);
3124 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3127 loadmodel->num = count;
3129 for (i = 0;i < count;i++, in++, out++)
3135 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3142 in = (void *)(mod_base + l->fileofs);
3143 if (l->filelen % sizeof(*in))
3144 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3145 count = l->filelen / sizeof(*in);
3146 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3149 loadmodel->num = count;
3151 for (i = 0;i < count;i++, in++, out++)
3157 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3164 in = (void *)(mod_base + l->fileofs);
3165 if (l->filelen % sizeof(*in))
3166 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3167 count = l->filelen / sizeof(*in);
3168 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3171 loadmodel->num = count;
3173 for (i = 0;i < count;i++, in++, out++)
3179 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3186 in = (void *)(mod_base + l->fileofs);
3187 if (l->filelen % sizeof(*in))
3188 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3189 count = l->filelen / sizeof(*in);
3190 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3193 loadmodel->num = count;
3195 for (i = 0;i < count;i++, in++, out++)
3201 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3208 in = (void *)(mod_base + l->fileofs);
3209 if (l->filelen % sizeof(*in))
3210 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3211 count = l->filelen / sizeof(*in);
3212 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3215 loadmodel->num = count;
3217 for (i = 0;i < count;i++, in++, out++)
3223 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3230 in = (void *)(mod_base + l->fileofs);
3231 if (l->filelen % sizeof(*in))
3232 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3233 count = l->filelen / sizeof(*in);
3234 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3237 loadmodel->num = count;
3239 for (i = 0;i < count;i++, in++, out++)
3245 static void Mod_Q2BSP_LoadModels(lump_t *l)
3252 in = (void *)(mod_base + l->fileofs);
3253 if (l->filelen % sizeof(*in))
3254 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3255 count = l->filelen / sizeof(*in);
3256 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3259 loadmodel->num = count;
3261 for (i = 0;i < count;i++, in++, out++)
3267 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3270 q2dheader_t *header;
3272 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3274 mod->type = mod_brushq2;
3276 header = (q2dheader_t *)buffer;
3278 i = LittleLong(header->version);
3279 if (i != Q2BSPVERSION)
3280 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3281 mod->brush.ishlbsp = false;
3282 if (loadmodel->isworldmodel)
3284 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3285 // until we get a texture for it...
3289 mod_base = (qbyte *)header;
3291 // swap all the lumps
3292 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3293 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3295 // store which lightmap format to use
3296 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3298 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3299 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3300 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3301 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3302 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3303 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3304 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3305 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3306 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3307 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3308 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3309 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3310 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3311 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3312 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3313 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3314 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3315 // LordHavoc: must go last because this makes the submodels
3316 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3319 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3320 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3322 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3325 char key[128], value[4096];
3327 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3328 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3329 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3332 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3333 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3334 data = loadmodel->brush.entities;
3335 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3336 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3340 if (!COM_ParseToken(&data, false))
3342 if (com_token[0] == '}')
3343 break; // end of worldspawn
3344 if (com_token[0] == '_')
3345 strcpy(key, com_token + 1);
3347 strcpy(key, com_token);
3348 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3349 key[strlen(key)-1] = 0;
3350 if (!COM_ParseToken(&data, false))
3352 strcpy(value, com_token);
3353 if (!strcmp("gridsize", key))
3355 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3356 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3362 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3372 char shadername[Q3PATHLENGTH];
3374 in = (void *)(mod_base + l->fileofs);
3375 if (l->filelen % sizeof(*in))
3376 Host_Error("Mod_Q3BSP_LoadTextures: 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_textures = out;
3381 loadmodel->brushq3.num_textures = count;
3383 for (i = 0;i < count;i++, in++, out++)
3386 strlcpy (out->name, in->name, sizeof (out->name));
3387 out->surfaceflags = LittleLong(in->surfaceflags);
3388 out->nativecontents = LittleLong(in->contents);
3389 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3390 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3391 out->surfaceparms = -1;
3394 // do a quick parse of shader files to get surfaceparms
3395 if ((search = FS_Search("scripts/*.shader", true, false)))
3397 for (i = 0;i < search->numfilenames;i++)
3399 if ((f = FS_LoadFile(search->filenames[i], false)))
3402 while (COM_ParseToken(&text, false))
3404 snprintf(shadername, sizeof(shadername), "%s", com_token);
3406 if (COM_ParseToken(&text, false) && !strcmp(com_token, "{"))
3408 while (COM_ParseToken(&text, false))
3410 if (!strcmp(com_token, "}"))
3412 else if (!strcmp(com_token, "{"))
3414 while (COM_ParseToken(&text, false))
3416 if (!strcmp(com_token, "}"))
3420 else if (!strcmp(com_token, "surfaceparm"))
3422 if (COM_ParseToken(&text, true) && strcmp(com_token, "\n"))
3424 if (!strcmp(com_token, "alphashadow"))
3425 flags |= Q3SURFACEPARM_ALPHASHADOW;
3426 else if (!strcmp(com_token, "areaportal"))
3427 flags |= Q3SURFACEPARM_AREAPORTAL;
3428 else if (!strcmp(com_token, "clusterportal"))
3429 flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3430 else if (!strcmp(com_token, "detail"))
3431 flags |= Q3SURFACEPARM_DETAIL;
3432 else if (!strcmp(com_token, "donotenter"))
3433 flags |= Q3SURFACEPARM_DONOTENTER;
3434 else if (!strcmp(com_token, "fog"))
3435 flags |= Q3SURFACEPARM_FOG;
3436 else if (!strcmp(com_token, "lava"))
3437 flags |= Q3SURFACEPARM_LAVA;
3438 else if (!strcmp(com_token, "lightfilter"))
3439 flags |= Q3SURFACEPARM_LIGHTFILTER;
3440 else if (!strcmp(com_token, "metalsteps"))
3441 flags |= Q3SURFACEPARM_METALSTEPS;
3442 else if (!strcmp(com_token, "nodamage"))
3443 flags |= Q3SURFACEPARM_NODAMAGE;
3444 else if (!strcmp(com_token, "nodlight"))
3445 flags |= Q3SURFACEPARM_NODLIGHT;
3446 else if (!strcmp(com_token, "nodraw"))
3447 flags |= Q3SURFACEPARM_NODRAW;
3448 else if (!strcmp(com_token, "nodrop"))
3449 flags |= Q3SURFACEPARM_NODROP;
3450 else if (!strcmp(com_token, "noimpact"))
3451 flags |= Q3SURFACEPARM_NOIMPACT;
3452 else if (!strcmp(com_token, "nolightmap"))
3453 flags |= Q3SURFACEPARM_NOLIGHTMAP;
3454 else if (!strcmp(com_token, "nomarks"))
3455 flags |= Q3SURFACEPARM_NOMARKS;
3456 else if (!strcmp(com_token, "nomipmaps"))
3457 flags |= Q3SURFACEPARM_NOMIPMAPS;
3458 else if (!strcmp(com_token, "nonsolid"))
3459 flags |= Q3SURFACEPARM_NONSOLID;
3460 else if (!strcmp(com_token, "origin"))
3461 flags |= Q3SURFACEPARM_ORIGIN;
3462 else if (!strcmp(com_token, "playerclip"))
3463 flags |= Q3SURFACEPARM_PLAYERCLIP;
3464 else if (!strcmp(com_token, "sky"))
3465 flags |= Q3SURFACEPARM_SKY;
3466 else if (!strcmp(com_token, "slick"))
3467 flags |= Q3SURFACEPARM_SLICK;
3468 else if (!strcmp(com_token, "slime"))
3469 flags |= Q3SURFACEPARM_SLIME;
3470 else if (!strcmp(com_token, "structural"))
3471 flags |= Q3SURFACEPARM_STRUCTURAL;
3472 else if (!strcmp(com_token, "trans"))
3473 flags |= Q3SURFACEPARM_TRANS;
3474 else if (!strcmp(com_token, "water"))
3475 flags |= Q3SURFACEPARM_WATER;
3477 Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
3478 if (!COM_ParseToken(&text, true) || strcmp(com_token, "\n"))
3480 Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
3486 Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
3492 // look for linebreak or }
3493 while(COM_ParseToken(&text, true) && strcmp(com_token, "\n") && strcmp(com_token, "}"));
3494 // break out to top level if it was }
3495 if (!strcmp(com_token, "}"))
3499 // add shader to list (shadername and flags)
3500 // actually here we just poke into the texture settings
3501 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3502 if (!strcmp(out->name, shadername))
3503 out->surfaceparms = flags;
3507 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3518 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3520 if (out->surfaceparms == -1)
3523 Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3524 out->surfaceparms = 0;
3525 // these are defaults
3526 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3527 || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3528 out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3529 if (!strncmp(out->name, "textures/skies/", 15))
3530 out->surfaceparms |= Q3SURFACEPARM_SKY;
3531 if (R_TextureHasAlpha(out->skin.base))
3532 out->surfaceparms |= Q3SURFACEPARM_TRANS;
3535 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3538 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3544 in = (void *)(mod_base + l->fileofs);
3545 if (l->filelen % sizeof(*in))
3546 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3547 count = l->filelen / sizeof(*in);
3548 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3550 loadmodel->brushq3.data_planes = out;
3551 loadmodel->brushq3.num_planes = count;
3553 for (i = 0;i < count;i++, in++, out++)
3555 out->normal[0] = LittleLong(in->normal[0]);
3556 out->normal[1] = LittleLong(in->normal[1]);
3557 out->normal[2] = LittleLong(in->normal[2]);
3558 out->dist = LittleLong(in->dist);
3563 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3566 q3mbrushside_t *out;
3569 in = (void *)(mod_base + l->fileofs);
3570 if (l->filelen % sizeof(*in))
3571 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3572 count = l->filelen / sizeof(*in);
3573 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3575 loadmodel->brushq3.data_brushsides = out;
3576 loadmodel->brushq3.num_brushsides = count;
3578 for (i = 0;i < count;i++, in++, out++)
3580 n = LittleLong(in->planeindex);
3581 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3582 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3583 out->plane = loadmodel->brushq3.data_planes + n;
3584 n = LittleLong(in->textureindex);
3585 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3586 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3587 out->texture = loadmodel->brushq3.data_textures + n;
3591 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3595 int i, j, n, c, count, maxplanes;
3597 winding_t *temp1, *temp2;
3599 in = (void *)(mod_base + l->fileofs);
3600 if (l->filelen % sizeof(*in))
3601 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3602 count = l->filelen / sizeof(*in);
3603 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3605 loadmodel->brushq3.data_brushes = out;
3606 loadmodel->brushq3.num_brushes = count;
3608 temp1 = Winding_New(64);
3609 temp2 = Winding_New(64);
3614 for (i = 0;i < count;i++, in++, out++)
3616 n = LittleLong(in->firstbrushside);
3617 c = LittleLong(in->numbrushsides);
3618 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3619 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3620 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3621 out->numbrushsides = c;
3622 n = LittleLong(in->textureindex);
3623 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3624 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3625 out->texture = loadmodel->brushq3.data_textures + n;
3627 // make a list of mplane_t structs to construct a colbrush from
3628 if (maxplanes < out->numbrushsides)
3630 maxplanes = out->numbrushsides;
3633 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3635 for (j = 0;j < out->numbrushsides;j++)
3637 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3638 planes[j].dist = out->firstbrushside[j].plane->dist;
3640 // make the colbrush from the planes
3641 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
3645 Winding_Free(temp1);
3646 Winding_Free(temp2);
3649 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3655 in = (void *)(mod_base + l->fileofs);
3656 if (l->filelen % sizeof(*in))
3657 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3658 count = l->filelen / sizeof(*in);
3659 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3661 loadmodel->brushq3.data_effects = out;
3662 loadmodel->brushq3.num_effects = count;
3664 for (i = 0;i < count;i++, in++, out++)
3666 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3667 n = LittleLong(in->brushindex);
3668 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3669 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3670 out->brush = loadmodel->brushq3.data_brushes + n;
3671 out->unknown = LittleLong(in->unknown);
3675 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3680 in = (void *)(mod_base + l->fileofs);
3681 if (l->filelen % sizeof(*in))
3682 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3683 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3684 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3685 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3686 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3687 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3688 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3689 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3690 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3692 for (i = 0;i < count;i++, in++)
3694 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3695 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3696 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3697 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3698 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3699 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3700 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3701 // svector/tvector are calculated later in face loading
3702 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3703 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3704 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3705 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3706 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3707 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3708 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3709 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3710 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3711 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3712 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3713 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3714 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3718 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3724 in = (void *)(mod_base + l->fileofs);
3725 if (l->filelen % sizeof(int[3]))
3726 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3727 count = l->filelen / sizeof(*in);
3728 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3730 loadmodel->brushq3.num_triangles = count / 3;
3731 loadmodel->brushq3.data_element3i = out;
3732 loadmodel->brushq3.data_neighbor3i = out + count;
3734 for (i = 0;i < count;i++, in++, out++)
3736 *out = LittleLong(*in);
3737 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3739 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3745 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3753 in = (void *)(mod_base + l->fileofs);
3754 if (l->filelen % sizeof(*in))
3755 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3756 count = l->filelen / sizeof(*in);
3757 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3759 loadmodel->brushq3.data_lightmaps = out;
3760 loadmodel->brushq3.num_lightmaps = count;
3762 for (i = 0;i < count;i++, in++, out++)
3763 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3766 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3770 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3771 //int *originalelement3i;
3772 //int *originalneighbor3i;
3773 float *originalvertex3f;
3774 //float *originalsvector3f;
3775 //float *originaltvector3f;
3776 //float *originalnormal3f;
3777 float *originalcolor4f;
3778 float *originaltexcoordtexture2f;
3779 float *originaltexcoordlightmap2f;
3782 in = (void *)(mod_base + l->fileofs);
3783 if (l->filelen % sizeof(*in))
3784 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3785 count = l->filelen / sizeof(*in);
3786 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3788 loadmodel->brushq3.data_faces = out;
3789 loadmodel->brushq3.num_faces = count;
3791 for (i = 0;i < count;i++, in++, out++)
3793 // check face type first
3794 out->type = LittleLong(in->type);
3795 if (out->type != Q3FACETYPE_POLYGON
3796 && out->type != Q3FACETYPE_PATCH
3797 && out->type != Q3FACETYPE_MESH
3798 && out->type != Q3FACETYPE_FLARE)
3800 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3801 out->num_vertices = 0;
3802 out->num_triangles = 0;
3803 out->type = 0; // error
3807 n = LittleLong(in->textureindex);
3808 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3810 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3811 out->num_vertices = 0;
3812 out->num_triangles = 0;
3813 out->type = 0; // error
3817 out->texture = loadmodel->brushq3.data_textures + n;
3818 n = LittleLong(in->effectindex);
3819 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3821 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3827 out->effect = loadmodel->brushq3.data_effects + n;
3828 n = LittleLong(in->lightmapindex);
3829 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3831 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3835 out->lightmaptexture = NULL;
3837 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3839 out->firstvertex = LittleLong(in->firstvertex);
3840 out->num_vertices = LittleLong(in->numvertices);
3841 out->firstelement = LittleLong(in->firstelement);
3842 out->num_triangles = LittleLong(in->numelements) / 3;
3843 if (out->num_triangles * 3 != LittleLong(in->numelements))
3845 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, LittleLong(in->numelements));
3846 out->num_vertices = 0;
3847 out->num_triangles = 0;
3848 out->type = 0; // error
3851 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3853 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->num_vertices, loadmodel->brushq3.num_vertices);
3854 out->num_vertices = 0;
3855 out->num_triangles = 0;
3856 out->type = 0; // error
3859 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3861 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->num_triangles * 3, loadmodel->brushq3.num_triangles * 3);
3862 out->num_vertices = 0;
3863 out->num_triangles = 0;
3864 out->type = 0; // error
3869 case Q3FACETYPE_POLYGON:
3870 case Q3FACETYPE_MESH:
3871 // no processing necessary
3872 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3873 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3874 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3875 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3876 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3877 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3878 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3879 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3880 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3882 case Q3FACETYPE_PATCH:
3883 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3884 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3885 if (patchsize[0] < 1 || patchsize[1] < 1)
3887 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3888 out->num_vertices = 0;
3889 out->num_triangles = 0;
3890 out->type = 0; // error
3893 // convert patch to Q3FACETYPE_MESH
3894 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3895 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3896 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3897 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3898 finalvertices = finalwidth * finalheight;
3899 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3900 originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3901 //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3902 //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3903 //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3904 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3905 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3906 originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3907 //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
3908 //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3910 originalvertex3f = out->data_vertex3f;
3911 //originalsvector3f = out->data_svector3f;
3912 //originaltvector3f = out->data_tvector3f;
3913 //originalnormal3f = out->data_normal3f;
3914 originalcolor4f = out->data_color4f;
3915 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3916 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3917 //originalelement3i = out->data_element3i;
3918 //originalneighbor3i = out->data_neighbor3i;
3920 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
3921 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3922 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3923 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3924 out->data_color4f = out->data_normal3f + finalvertices * 3;
3925 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3926 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3927 out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
3928 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3929 out->type = Q3FACETYPE_MESH;
3930 out->firstvertex = -1;
3931 out->num_vertices = finalvertices;
3932 out->firstelement = -1;
3933 out->num_triangles = finaltriangles;
3934 // generate geometry
3935 // (note: normals are skipped because they get recalculated)
3936 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3937 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3938 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3939 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3940 // generate elements
3941 e = out->data_element3i;
3942 for (y = 0;y < finalheight - 1;y++)
3944 row0 = (y + 0) * finalwidth;
3945 row1 = (y + 1) * finalwidth;
3946 for (x = 0;x < finalwidth - 1;x++)
3958 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3959 if (developer.integer)
3961 if (out->num_triangles < finaltriangles)
3962 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_vertices, finaltriangles, finaltriangles - out->num_triangles, out->num_triangles);
3964 Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_vertices, out->num_triangles);
3966 // q3map does not put in collision brushes for curves... ugh
3967 out->collisions = true;
3969 case Q3FACETYPE_FLARE:
3970 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3972 out->num_vertices = 0;
3973 out->num_triangles = 0;
3977 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
3978 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3980 if (invalidelements)
3982 Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->num_vertices, out->firstelement, out->num_triangles * 3);
3983 for (j = 0;j < out->num_triangles * 3;j++)
3985 Con_Printf(" %i", out->data_element3i[j]);
3986 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3987 out->data_element3i[j] = 0;
3991 // for shadow volumes
3992 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
3993 // for per pixel lighting
3994 Mod_BuildTextureVectorsAndNormals(out->num_vertices, out->num_triangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
3995 // calculate a bounding box
3996 VectorClear(out->mins);
3997 VectorClear(out->maxs);
3998 if (out->num_vertices)
4000 VectorCopy(out->data_vertex3f, out->mins);
4001 VectorCopy(out->data_vertex3f, out->maxs);
4002 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
4004 out->mins[0] = min(out->mins[0], v[0]);
4005 out->maxs[0] = max(out->maxs[0], v[0]);
4006 out->mins[1] = min(out->mins[1], v[1]);
4007 out->maxs[1] = max(out->maxs[1], v[1]);
4008 out->mins[2] = min(out->mins[2], v[2]);
4009 out->maxs[2] = max(out->maxs[2], v[2]);
4011 out->mins[0] -= 1.0f;
4012 out->mins[1] -= 1.0f;
4013 out->mins[2] -= 1.0f;
4014 out->maxs[0] += 1.0f;
4015 out->maxs[1] += 1.0f;
4016 out->maxs[2] += 1.0f;
4020 // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
4023 int totalverts, totaltris;
4024 int originalnum_vertices;
4025 float *originaldata_vertex3f;
4026 float *originaldata_texcoordtexture2f;
4027 float *originaldata_texcoordlightmap2f;
4028 float *originaldata_svector3f;
4029 float *originaldata_tvector3f;
4030 float *originaldata_normal3f;
4031 float *originaldata_color4f;
4032 int originalnum_triangles;
4033 int *originaldata_element3i;
4034 int *originaldata_neighbor3i;
4038 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4042 totalverts += out->num_vertices;
4043 totaltris += out->num_triangles;
4046 originalnum_vertices = loadmodel->brushq3.num_vertices;
4047 originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
4048 originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
4049 originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
4050 originaldata_svector3f = loadmodel->brushq3.data_svector3f;
4051 originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
4052 originaldata_normal3f = loadmodel->brushq3.data_normal3f;
4053 originaldata_color4f = loadmodel->brushq3.data_color4f;
4054 originalnum_triangles = loadmodel->brushq3.num_triangles;
4055 originaldata_element3i = loadmodel->brushq3.data_element3i;
4056 originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
4057 loadmodel->brushq3.num_vertices = totalverts;
4058 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
4059 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
4060 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
4061 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
4062 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
4063 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
4064 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
4065 loadmodel->brushq3.num_triangles = totaltris;
4066 loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
4067 loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
4070 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4074 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
4075 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
4076 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
4077 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
4078 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
4079 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
4080 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
4081 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
4082 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
4083 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
4084 if (out->firstvertex == -1)
4085 Mem_Free(out->data_vertex3f);
4086 if (out->firstelement == -1)
4087 Mem_Free(out->data_element3i);
4088 out->firstvertex = totalverts;
4089 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4090 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4091 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4092 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4093 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4094 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4095 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4096 out->firstelement = totaltris * 3;
4097 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4098 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4099 //for (j = 0;j < out->numtriangles * 3;j++)
4100 // out->data_element3i[j] += totalverts - out->firstvertex;
4101 totalverts += out->num_vertices;
4102 totaltris += out->num_triangles;
4104 Mem_Free(originaldata_vertex3f);
4105 Mem_Free(originaldata_element3i);
4110 static void Mod_Q3BSP_LoadModels(lump_t *l)
4114 int i, j, n, c, count;
4116 in = (void *)(mod_base + l->fileofs);
4117 if (l->filelen % sizeof(*in))
4118 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4119 count = l->filelen / sizeof(*in);
4120 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4122 loadmodel->brushq3.data_models = out;
4123 loadmodel->brushq3.num_models = count;
4125 for (i = 0;i < count;i++, in++, out++)
4127 for (j = 0;j < 3;j++)
4129 out->mins[j] = LittleFloat(in->mins[j]);
4130 out->maxs[j] = LittleFloat(in->maxs[j]);
4132 n = LittleLong(in->firstface);
4133 c = LittleLong(in->numfaces);
4134 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4135 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4136 out->firstface = loadmodel->brushq3.data_faces + n;
4138 n = LittleLong(in->firstbrush);
4139 c = LittleLong(in->numbrushes);
4140 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4141 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4142 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4143 out->numbrushes = c;
4147 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4153 in = (void *)(mod_base + l->fileofs);
4154 if (l->filelen % sizeof(*in))
4155 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4156 count = l->filelen / sizeof(*in);
4157 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4159 loadmodel->brushq3.data_leafbrushes = out;
4160 loadmodel->brushq3.num_leafbrushes = count;
4162 for (i = 0;i < count;i++, in++, out++)
4164 n = LittleLong(*in);
4165 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4166 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4167 *out = loadmodel->brushq3.data_brushes + n;
4171 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4177 in = (void *)(mod_base + l->fileofs);
4178 if (l->filelen % sizeof(*in))
4179 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4180 count = l->filelen / sizeof(*in);
4181 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4183 loadmodel->brushq3.data_leaffaces = out;
4184 loadmodel->brushq3.num_leaffaces = count;
4186 for (i = 0;i < count;i++, in++, out++)
4188 n = LittleLong(*in);
4189 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4190 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4191 *out = loadmodel->brushq3.data_faces + n;
4195 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4199 int i, j, n, c, count;
4201 in = (void *)(mod_base + l->fileofs);
4202 if (l->filelen % sizeof(*in))
4203 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4204 count = l->filelen / sizeof(*in);
4205 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4207 loadmodel->brushq3.data_leafs = out;
4208 loadmodel->brushq3.num_leafs = count;
4210 for (i = 0;i < count;i++, in++, out++)
4212 out->isnode = false;
4214 out->clusterindex = LittleLong(in->clusterindex);
4215 out->areaindex = LittleLong(in->areaindex);
4216 for (j = 0;j < 3;j++)
4218 // yes the mins/maxs are ints
4219 out->mins[j] = LittleLong(in->mins[j]);
4220 out->maxs[j] = LittleLong(in->maxs[j]);
4222 n = LittleLong(in->firstleafface);
4223 c = LittleLong(in->numleaffaces);
4224 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4225 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4226 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4227 out->numleaffaces = c;
4228 n = LittleLong(in->firstleafbrush);
4229 c = LittleLong(in->numleafbrushes);
4230 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4231 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4232 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4233 out->numleafbrushes = c;
4237 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4240 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4241 node->parent = parent;
4244 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4245 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4249 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4255 in = (void *)(mod_base + l->fileofs);
4256 if (l->filelen % sizeof(*in))
4257 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4258 count = l->filelen / sizeof(*in);
4259 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4261 loadmodel->brushq3.data_nodes = out;
4262 loadmodel->brushq3.num_nodes = count;
4264 for (i = 0;i < count;i++, in++, out++)
4268 n = LittleLong(in->planeindex);
4269 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4270 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4271 out->plane = loadmodel->brushq3.data_planes + n;
4272 for (j = 0;j < 2;j++)
4274 n = LittleLong(in->childrenindex[j]);
4277 if (n >= loadmodel->brushq3.num_nodes)
4278 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4279 out->children[j] = loadmodel->brushq3.data_nodes + n;
4284 if (n >= loadmodel->brushq3.num_leafs)
4285 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4286 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4289 for (j = 0;j < 3;j++)
4291 // yes the mins/maxs are ints
4292 out->mins[j] = LittleLong(in->mins[j]);
4293 out->maxs[j] = LittleLong(in->maxs[j]);
4297 // set the parent pointers
4298 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4301 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4304 q3dlightgrid_t *out;
4307 if (l->filelen == 0)
4310 in = (void *)(mod_base + l->fileofs);
4311 if (l->filelen % sizeof(*in))
4312 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4313 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4314 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4315 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4316 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4317 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4318 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4319 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4320 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4321 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4322 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4323 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4324 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4325 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4326 if (l->filelen < count * (int)sizeof(*in))
4327 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]);
4328 if (l->filelen != count * (int)sizeof(*in))
4329 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4331 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4332 loadmodel->brushq3.data_lightgrid = out;
4333 loadmodel->brushq3.num_lightgrid = count;
4335 // no swapping or validation necessary
4336 memcpy(out, in, count * (int)sizeof(*out));
4338 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]);
4339 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]);
4342 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4347 if (l->filelen == 0)
4350 in = (void *)(mod_base + l->fileofs);
4352 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4354 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4355 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4356 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4357 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4358 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4359 if (l->filelen < totalchains + (int)sizeof(*in))
4360 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);
4362 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4363 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4366 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4368 // FIXME: finish this code
4369 VectorCopy(in, out);
4372 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4374 int i, j, k, index[3];
4375 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4376 q3dlightgrid_t *a, *s;
4377 // FIXME: write this
4378 if (!model->brushq3.num_lightgrid)
4380 ambientcolor[0] = 1;
4381 ambientcolor[1] = 1;
4382 ambientcolor[2] = 1;
4385 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4386 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4387 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4388 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4389 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4390 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4391 index[0] = (int)floor(transformed[0]);
4392 index[1] = (int)floor(transformed[1]);
4393 index[2] = (int)floor(transformed[2]);
4394 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4395 // now lerp the values
4396 VectorClear(diffusenormal);
4397 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4398 for (k = 0;k < 2;k++)
4400 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4401 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4403 for (j = 0;j < 2;j++)
4405 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4406 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4408 for (i = 0;i < 2;i++)
4410 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4411 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4413 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4414 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4415 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4416 pitch = s->diffusepitch * M_PI / 128;
4417 yaw = s->diffuseyaw * M_PI / 128;
4418 sinpitch = sin(pitch);
4419 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4420 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4421 diffusenormal[2] += blend * (cos(pitch));
4422 //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)));
4426 VectorNormalize(diffusenormal);
4427 //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]);
4430 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)
4432 int i, startside, endside;
4433 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4437 if (startfrac >= trace->fraction)
4439 // note: all line fragments past first impact fraction are ignored
4440 while (node->isnode)
4442 // recurse down node sides
4443 dist1 = PlaneDiff(start, node->plane);
4444 dist2 = PlaneDiff(end, node->plane);
4445 startside = dist1 < 0;
4446 endside = dist2 < 0;
4447 if (startside == endside)
4449 // most of the time the line fragment is on one side of the plane
4450 node = node->children[startside];
4454 // line crosses node plane, split the line
4455 midfrac = dist1 / (dist1 - dist2);
4456 VectorLerp(linestart, midfrac, lineend, mid);
4457 // take the near side first
4458 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4459 if (midfrac < trace->fraction)
4460 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4465 segmentmins[0] = min(start[0], end[0]);
4466 segmentmins[1] = min(start[1], end[1]);
4467 segmentmins[2] = min(start[2], end[2]);
4468 segmentmaxs[0] = max(start[0], end[0]);
4469 segmentmaxs[1] = max(start[1], end[1]);
4470 segmentmaxs[2] = max(start[2], end[2]);
4471 leaf = (q3mleaf_t *)node;
4472 for (i = 0;i < leaf->numleafbrushes;i++)
4474 if (startfrac >= trace->fraction)
4476 brush = leaf->firstleafbrush[i]->colbrushf;
4477 if (brush && brush->markframe != markframe)
4479 brush->markframe = markframe;
4480 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4481 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4484 if (mod_q3bsp_curves_collisions.integer)
4486 for (i = 0;i < leaf->numleaffaces;i++)
4488 if (startfrac >= trace->fraction)
4490 face = leaf->firstleafface[i];
4491 if (face->collisions && face->collisionmarkframe != markframe)
4493 face->collisionmarkframe = markframe;
4494 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4495 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4501 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)
4504 float nodesegmentmins[3], nodesegmentmaxs[3];
4508 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4509 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4510 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4511 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4512 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4513 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4514 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4518 // recurse down node sides
4519 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4522 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4523 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4525 else if (sides == 2)
4526 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4528 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4530 dist = node->plane->dist - (1.0f / 8.0f);
4531 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4533 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4535 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4541 dist = node->plane->dist + (1.0f / 8.0f);
4542 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4544 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4546 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4552 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4554 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4556 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4562 leaf = (q3mleaf_t *)node;
4563 for (i = 0;i < leaf->numleafbrushes;i++)
4565 brush = leaf->firstleafbrush[i]->colbrushf;
4566 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4568 brush->markframe = markframe;
4569 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4572 if (mod_q3bsp_curves_collisions.integer)
4574 for (i = 0;i < leaf->numleaffaces;i++)
4576 face = leaf->firstleafface[i];
4577 // 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
4578 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4579 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4585 static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
4588 float segmentmins[3], segmentmaxs[3];
4589 colbrushf_t *thisbrush_start, *thisbrush_end;
4590 matrix4x4_t startmatrix, endmatrix;
4591 static int markframe = 0;
4593 memset(trace, 0, sizeof(*trace));
4594 trace->fraction = 1;
4595 trace->hitsupercontentsmask = hitsupercontentsmask;
4596 Matrix4x4_CreateIdentity(&startmatrix);
4597 Matrix4x4_CreateIdentity(&endmatrix);
4598 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4599 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4600 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4601 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4602 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4603 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4604 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4607 if (model->brushq3.submodel)
4609 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4610 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4611 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4612 if (mod_q3bsp_curves_collisions.integer)
4614 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4616 face = model->brushq3.data_thismodel->firstface + i;
4617 if (face->collisions)
4618 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4623 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4627 // box trace, performed as brush trace
4628 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4629 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4630 if (model->brushq3.submodel)
4632 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4633 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4634 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4635 if (mod_q3bsp_curves_collisions.integer)
4637 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4639 face = model->brushq3.data_thismodel->firstface + i;
4640 if (face->collisions)
4641 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4646 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4651 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)
4658 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4659 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4662 // node - recurse down the BSP tree
4663 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4666 node = node->children[0];
4669 node = node->children[1];
4671 default: // crossing
4672 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4674 node = node->children[1];
4681 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4683 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4686 //Returns PVS data for a given point
4687 //(note: can return NULL)
4688 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4691 Mod_CheckLoaded(model);
4692 node = model->brushq3.data_nodes;
4693 while (node->isnode)
4694 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4695 if (((q3mleaf_t *)node)->clusterindex >= 0)
4696 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4701 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4707 while (node->isnode)
4709 d = PlaneDiff(org, node->plane);
4711 node = node->children[0];
4712 else if (d < -radius)
4713 node = node->children[1];
4716 // go down both sides
4717 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4718 node = node->children[1];
4721 // if this is a leaf with a pvs, accumulate the pvs bits
4722 if (((q3mleaf_t *)node)->clusterindex >= 0)
4724 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4725 for (i = 0;i < pvsbytes;i++)
4726 pvsbuffer[i] |= pvs[i];
4731 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4732 //of the given point.
4733 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4735 int bytes = model->brushq3.num_pvschainlength;
4736 bytes = min(bytes, pvsbufferlength);
4737 memset(pvsbuffer, 0, bytes);
4738 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4743 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4745 int supercontents = 0;
4746 if (nativecontents & Q2CONTENTS_SOLID)
4747 supercontents |= SUPERCONTENTS_SOLID;
4748 if (nativecontents & Q2CONTENTS_WATER)
4749 supercontents |= SUPERCONTENTS_WATER;
4750 if (nativecontents & Q2CONTENTS_SLIME)
4751 supercontents |= SUPERCONTENTS_SLIME;
4752 if (nativecontents & Q2CONTENTS_LAVA)
4753 supercontents |= SUPERCONTENTS_LAVA;
4754 return supercontents;
4757 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4759 int nativecontents = 0;
4760 if (supercontents & SUPERCONTENTS_SOLID)
4761 nativecontents |= Q2CONTENTS_SOLID;
4762 if (supercontents & SUPERCONTENTS_WATER)
4763 nativecontents |= Q2CONTENTS_WATER;
4764 if (supercontents & SUPERCONTENTS_SLIME)
4765 nativecontents |= Q2CONTENTS_SLIME;
4766 if (supercontents & SUPERCONTENTS_LAVA)
4767 nativecontents |= Q2CONTENTS_LAVA;
4768 return nativecontents;
4771 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4772 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4773 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4774 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);
4775 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4778 q3dheader_t *header;
4779 float corner[3], yawradius, modelradius;
4781 mod->type = mod_brushq3;
4785 header = (q3dheader_t *)buffer;
4787 i = LittleLong(header->version);
4788 if (i != Q3BSPVERSION)
4789 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4790 if (loadmodel->isworldmodel)
4792 Cvar_SetValue("halflifebsp", false);
4793 // until we get a texture for it...
4797 mod->soundfromcenter = true;
4798 mod->TraceBox = Mod_Q3BSP_TraceBox;
4799 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4800 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4801 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4802 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4803 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4804 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4805 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4806 //mod->DrawSky = R_Q3BSP_DrawSky;
4807 mod->Draw = R_Q3BSP_Draw;
4808 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4809 mod->DrawLight = R_Q3BSP_DrawLight;
4811 mod_base = (qbyte *)header;
4813 // swap all the lumps
4814 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4815 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4817 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4818 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4819 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4820 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4821 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4822 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4823 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4824 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4825 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4826 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4827 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4828 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4829 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4830 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4831 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4832 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4833 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4834 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4836 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4843 // LordHavoc: only register submodels if it is the world
4844 // (prevents bsp models from replacing world submodels)
4845 if (!loadmodel->isworldmodel)
4847 // duplicate the basic information
4848 sprintf(name, "*%i", i);
4849 mod = Mod_FindName(name);
4851 strcpy(mod->name, name);
4852 // textures and memory belong to the main model
4853 mod->texturepool = NULL;
4854 mod->mempool = NULL;
4856 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4857 mod->brushq3.submodel = i;
4859 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4860 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4861 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4862 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4863 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4864 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4865 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4866 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4867 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4868 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4869 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4870 mod->yawmins[2] = mod->normalmins[2];
4871 mod->yawmaxs[2] = mod->normalmaxs[2];
4872 mod->radius = modelradius;
4873 mod->radius2 = modelradius * modelradius;
4875 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
4876 if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
4878 if (j < mod->brushq3.data_thismodel->numfaces)
4879 mod->DrawSky = R_Q3BSP_DrawSky;
4883 void Mod_IBSP_Load(model_t *mod, void *buffer)
4885 int i = LittleLong(((int *)buffer)[1]);
4886 if (i == Q3BSPVERSION)
4887 Mod_Q3BSP_Load(mod,buffer);
4888 else if (i == Q2BSPVERSION)
4889 Mod_Q2BSP_Load(mod,buffer);
4891 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4894 void Mod_MAP_Load(model_t *mod, void *buffer)
4896 Host_Error("Mod_MAP_Load: not yet implemented\n");