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 return HULLCHECKSTATE_SOLID;
423 t->trace->allsolid = false;
424 return HULLCHECKSTATE_EMPTY;
428 // find the point distances
429 node = t->hull->clipnodes + num;
431 plane = t->hull->planes + node->planenum;
434 t1 = p1[plane->type] - plane->dist;
435 t2 = p2[plane->type] - plane->dist;
439 t1 = DotProduct (plane->normal, p1) - plane->dist;
440 t2 = DotProduct (plane->normal, p2) - plane->dist;
447 num = node->children[1];
456 num = node->children[0];
462 // the line intersects, find intersection point
463 // LordHavoc: this uses the original trace for maximum accuracy
466 t1 = t->start[plane->type] - plane->dist;
467 t2 = t->end[plane->type] - plane->dist;
471 t1 = DotProduct (plane->normal, t->start) - plane->dist;
472 t2 = DotProduct (plane->normal, t->end) - plane->dist;
475 midf = t1 / (t1 - t2);
476 midf = bound(p1f, midf, p2f);
477 VectorMA(t->start, midf, t->dist, mid);
479 // recurse both sides, front side first
480 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
481 // if this side is not empty, return what it is (solid or done)
482 if (ret != HULLCHECKSTATE_EMPTY)
485 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
486 // if other side is not solid, return what it is (empty or done)
487 if (ret != HULLCHECKSTATE_SOLID)
490 // front is air and back is solid, this is the impact point...
493 t->trace->plane.dist = -plane->dist;
494 VectorNegate (plane->normal, t->trace->plane.normal);
498 t->trace->plane.dist = plane->dist;
499 VectorCopy (plane->normal, t->trace->plane.normal);
502 // bias away from surface a bit
503 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
504 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
506 midf = t1 / (t1 - t2);
507 t->trace->fraction = bound(0.0f, midf, 1.0);
509 return HULLCHECKSTATE_DONE;
512 static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
514 // this function currently only supports same size start and end
516 RecursiveHullCheckTraceInfo_t rhc;
518 memset(&rhc, 0, sizeof(rhc));
519 memset(trace, 0, sizeof(trace_t));
521 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
522 rhc.trace->fraction = 1;
523 rhc.trace->allsolid = true;
524 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
526 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
527 else if (model->brush.ishlbsp)
529 if (boxsize[0] <= 32)
531 if (boxsize[2] < 54) // pick the nearest of 36 or 72
532 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
534 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
537 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
541 if (boxsize[0] <= 32)
542 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
544 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
546 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
547 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
548 VectorSubtract(rhc.end, rhc.start, rhc.dist);
549 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
552 static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
554 int side, distz = endz - startz;
559 if (node->contents < 0)
560 return false; // didn't hit anything
562 switch (node->plane->type)
565 node = node->children[x < node->plane->dist];
568 node = node->children[y < node->plane->dist];
571 side = startz < node->plane->dist;
572 if ((endz < node->plane->dist) == side)
574 node = node->children[side];
577 // found an intersection
578 mid = node->plane->dist;
581 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
582 front += startz * node->plane->normal[2];
583 back += endz * node->plane->normal[2];
584 side = front < node->plane->dist;
585 if ((back < node->plane->dist) == side)
587 node = node->children[side];
590 // found an intersection
591 mid = startz + distz * (front - node->plane->dist) / (front - back);
595 // go down front side
596 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
597 return true; // hit something
600 // check for impact on this node
601 if (node->numsurfaces)
606 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
607 for (i = 0;i < node->numsurfaces;i++, surf++)
609 if (!(surf->flags & SURF_LIGHTMAP))
610 continue; // no lightmaps
612 ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3]);
613 dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]);
615 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
618 ds -= surf->texturemins[0];
619 dt -= surf->texturemins[1];
621 if (ds > surf->extents[0] || dt > surf->extents[1])
627 int maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
628 line3 = ((surf->extents[0]>>4)+1)*3;
629 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
631 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
633 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
635 scale = d_lightstylevalue[surf->styles[maps]];
636 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
637 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
638 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
639 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
644 LordHavoc: here's the readable version of the interpolation
645 code, not quite as easy for the compiler to optimize...
647 dsfrac is the X position in the lightmap pixel, * 16
648 dtfrac is the Y position in the lightmap pixel, * 16
649 r00 is top left corner, r01 is top right corner
650 r10 is bottom left corner, r11 is bottom right corner
651 g and b are the same layout.
652 r0 and r1 are the top and bottom intermediate results
654 first we interpolate the top two points, to get the top
657 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
658 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
659 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
661 then we interpolate the bottom two points, to get the
664 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
665 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
666 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
668 then we interpolate the top and bottom samples to get the
669 middle sample (the one which was requested)
671 r = (((r1-r0) * dtfrac) >> 4) + r0;
672 g = (((g1-g0) * dtfrac) >> 4) + g0;
673 b = (((b1-b0) * dtfrac) >> 4) + b0;
676 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
677 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
678 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
680 return true; // success
685 node = node->children[side ^ 1];
687 distz = endz - startz;
692 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
694 Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, cl.worldmodel->brushq1.nodes + cl.worldmodel->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
697 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
704 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
714 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
717 for (c = *in++;c > 0;c--)
721 Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\"\n", loadmodel->name);
730 static void Mod_Q1BSP_LoadTextures(lump_t *l)
732 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
734 texture_t *tx, *tx2, *anims[10], *altanims[10];
736 qbyte *data, *mtdata;
739 loadmodel->brushq1.textures = NULL;
741 // add two slots for notexture walls and notexture liquids
744 m = (dmiptexlump_t *)(mod_base + l->fileofs);
745 m->nummiptex = LittleLong (m->nummiptex);
746 loadmodel->brushq1.numtextures = m->nummiptex + 2;
751 loadmodel->brushq1.numtextures = 2;
754 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
756 // fill out all slots with notexture
757 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
760 strcpy(tx->name, "NO TEXTURE FOUND");
763 tx->skin.base = r_notexture;
764 tx->shader = &Cshader_wall_lightmap;
765 tx->flags = SURF_SOLIDCLIP;
766 if (i == loadmodel->brushq1.numtextures - 1)
768 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
769 tx->shader = &Cshader_water;
771 tx->currentframe = tx;
777 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
779 // LordHavoc: mostly rewritten map texture loader
780 for (i = 0;i < m->nummiptex;i++)
782 dofs[i] = LittleLong(dofs[i]);
783 if (dofs[i] == -1 || r_nosurftextures.integer)
785 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
787 // make sure name is no more than 15 characters
788 for (j = 0;dmiptex->name[j] && j < 15;j++)
789 name[j] = dmiptex->name[j];
792 mtwidth = LittleLong(dmiptex->width);
793 mtheight = LittleLong(dmiptex->height);
795 j = LittleLong(dmiptex->offsets[0]);
799 if (j < 40 || j + mtwidth * mtheight > l->filelen)
801 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
804 mtdata = (qbyte *)dmiptex + j;
807 if ((mtwidth & 15) || (mtheight & 15))
808 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
810 // LordHavoc: force all names to lowercase
811 for (j = 0;name[j];j++)
812 if (name[j] >= 'A' && name[j] <= 'Z')
813 name[j] += 'a' - 'A';
815 tx = loadmodel->brushq1.textures + i;
816 strcpy(tx->name, name);
818 tx->height = mtheight;
822 sprintf(tx->name, "unnamed%i", i);
823 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
826 // LordHavoc: HL sky textures are entirely different than quake
827 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
829 if (loadmodel->isworldmodel)
831 data = loadimagepixels(tx->name, false, 0, 0);
834 if (image_width == 256 && image_height == 128)
842 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
844 R_InitSky(mtdata, 1);
847 else if (mtdata != NULL)
848 R_InitSky(mtdata, 1);
853 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
855 // did not find external texture, load it from the bsp or wad3
856 if (loadmodel->brush.ishlbsp)
858 // internal texture overrides wad
859 qbyte *pixels, *freepixels, *fogpixels;
860 pixels = freepixels = NULL;
862 pixels = W_ConvertWAD3Texture(dmiptex);
864 pixels = freepixels = W_GetTexture(tx->name);
867 tx->width = image_width;
868 tx->height = image_height;
869 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);
870 if (Image_CheckAlpha(pixels, image_width * image_height, true))
872 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
873 for (j = 0;j < image_width * image_height * 4;j += 4)
875 fogpixels[j + 0] = 255;
876 fogpixels[j + 1] = 255;
877 fogpixels[j + 2] = 255;
878 fogpixels[j + 3] = pixels[j + 3];
880 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
885 Mem_Free(freepixels);
887 else if (mtdata) // texture included
888 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
891 if (tx->skin.base == NULL)
896 tx->skin.base = r_notexture;
899 if (tx->name[0] == '*')
901 // turb does not block movement
902 tx->flags &= ~SURF_SOLIDCLIP;
903 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
904 // LordHavoc: some turbulent textures should be fullbright and solid
905 if (!strncmp(tx->name,"*lava",5)
906 || !strncmp(tx->name,"*teleport",9)
907 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
908 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
910 tx->flags |= SURF_WATERALPHA;
911 tx->shader = &Cshader_water;
913 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
915 tx->flags |= SURF_DRAWSKY;
916 tx->shader = &Cshader_sky;
920 tx->flags |= SURF_LIGHTMAP;
922 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
923 tx->shader = &Cshader_wall_lightmap;
926 // start out with no animation
927 tx->currentframe = tx;
930 // sequence the animations
931 for (i = 0;i < m->nummiptex;i++)
933 tx = loadmodel->brushq1.textures + i;
934 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
936 if (tx->anim_total[0] || tx->anim_total[1])
937 continue; // already sequenced
939 // find the number of frames in the animation
940 memset(anims, 0, sizeof(anims));
941 memset(altanims, 0, sizeof(altanims));
943 for (j = i;j < m->nummiptex;j++)
945 tx2 = loadmodel->brushq1.textures + j;
946 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
950 if (num >= '0' && num <= '9')
951 anims[num - '0'] = tx2;
952 else if (num >= 'a' && num <= 'j')
953 altanims[num - 'a'] = tx2;
955 Con_Printf("Bad animating texture %s\n", tx->name);
959 for (j = 0;j < 10;j++)
966 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
969 for (j = 0;j < max;j++)
973 Con_Printf("Missing frame %i of %s\n", j, tx->name);
977 for (j = 0;j < altmax;j++)
981 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
990 // if there is no alternate animation, duplicate the primary
991 // animation into the alternate
993 for (k = 0;k < 10;k++)
994 altanims[k] = anims[k];
997 // link together the primary animation
998 for (j = 0;j < max;j++)
1001 tx2->animated = true;
1002 tx2->anim_total[0] = max;
1003 tx2->anim_total[1] = altmax;
1004 for (k = 0;k < 10;k++)
1006 tx2->anim_frames[0][k] = anims[k];
1007 tx2->anim_frames[1][k] = altanims[k];
1011 // if there really is an alternate anim...
1012 if (anims[0] != altanims[0])
1014 // link together the alternate animation
1015 for (j = 0;j < altmax;j++)
1018 tx2->animated = true;
1019 // the primary/alternate are reversed here
1020 tx2->anim_total[0] = altmax;
1021 tx2->anim_total[1] = max;
1022 for (k = 0;k < 10;k++)
1024 tx2->anim_frames[0][k] = altanims[k];
1025 tx2->anim_frames[1][k] = anims[k];
1032 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1035 qbyte *in, *out, *data, d;
1036 char litfilename[1024];
1037 loadmodel->brushq1.lightdata = NULL;
1038 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1040 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1041 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1043 else // LordHavoc: bsp version 29 (normal white lighting)
1045 // LordHavoc: hope is not lost yet, check for a .lit file to load
1046 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1047 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1048 strlcat (litfilename, ".lit", sizeof (litfilename));
1049 data = (qbyte*) FS_LoadFile(litfilename, false);
1052 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1054 i = LittleLong(((int *)data)[1]);
1057 Con_DPrintf("loaded %s\n", litfilename);
1058 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1059 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1065 Con_Printf("Unknown .lit file version (%d)\n", i);
1071 if (fs_filesize == 8)
1072 Con_Printf("Empty .lit file, ignoring\n");
1074 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1078 // LordHavoc: oh well, expand the white lighting data
1081 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1082 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1083 out = loadmodel->brushq1.lightdata;
1084 memcpy(in, mod_base + l->fileofs, l->filelen);
1085 for (i = 0;i < l->filelen;i++)
1095 static void Mod_Q1BSP_LoadLightList(void)
1097 int a, n, numlights;
1098 char lightsfilename[1024], *s, *t, *lightsstring;
1101 strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1102 FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1103 strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1104 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1110 while (*s && *s != '\n')
1114 Mem_Free(lightsstring);
1115 Host_Error("lights file must end with a newline\n");
1120 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1123 while (*s && n < numlights)
1126 while (*s && *s != '\n')
1130 Mem_Free(lightsstring);
1131 Host_Error("misparsed lights file!\n");
1133 e = loadmodel->brushq1.lights + n;
1135 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);
1139 Mem_Free(lightsstring);
1140 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);
1147 Mem_Free(lightsstring);
1148 Host_Error("misparsed lights file!\n");
1150 loadmodel->brushq1.numlights = numlights;
1151 Mem_Free(lightsstring);
1155 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1157 loadmodel->brushq1.num_compressedpvs = 0;
1158 loadmodel->brushq1.data_compressedpvs = NULL;
1161 loadmodel->brushq1.num_compressedpvs = l->filelen;
1162 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1163 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1166 // used only for HalfLife maps
1167 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1169 char key[128], value[4096];
1174 if (!COM_ParseToken(&data, false))
1176 if (com_token[0] != '{')
1180 if (!COM_ParseToken(&data, false))
1182 if (com_token[0] == '}')
1183 break; // end of worldspawn
1184 if (com_token[0] == '_')
1185 strcpy(key, com_token + 1);
1187 strcpy(key, com_token);
1188 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1189 key[strlen(key)-1] = 0;
1190 if (!COM_ParseToken(&data, false))
1192 strcpy(value, com_token);
1193 if (!strcmp("wad", key)) // for HalfLife maps
1195 if (loadmodel->brush.ishlbsp)
1198 for (i = 0;i < 4096;i++)
1199 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1205 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1206 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1208 else if (value[i] == ';' || value[i] == 0)
1212 strcpy(wadname, "textures/");
1213 strcat(wadname, &value[j]);
1214 W_LoadTextureWadFile(wadname, false);
1226 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1228 loadmodel->brush.entities = NULL;
1231 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1232 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1233 if (loadmodel->brush.ishlbsp)
1234 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1238 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1244 in = (void *)(mod_base + l->fileofs);
1245 if (l->filelen % sizeof(*in))
1246 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1247 count = l->filelen / sizeof(*in);
1248 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1250 loadmodel->brushq1.vertexes = out;
1251 loadmodel->brushq1.numvertexes = count;
1253 for ( i=0 ; i<count ; i++, in++, out++)
1255 out->position[0] = LittleFloat(in->point[0]);
1256 out->position[1] = LittleFloat(in->point[1]);
1257 out->position[2] = LittleFloat(in->point[2]);
1261 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1267 in = (void *)(mod_base + l->fileofs);
1268 if (l->filelen % sizeof(*in))
1269 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1270 count = l->filelen / sizeof(*in);
1271 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1273 loadmodel->brushq1.submodels = out;
1274 loadmodel->brush.numsubmodels = count;
1276 for ( i=0 ; i<count ; i++, in++, out++)
1278 for (j=0 ; j<3 ; j++)
1280 // spread the mins / maxs by a pixel
1281 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1282 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1283 out->origin[j] = LittleFloat(in->origin[j]);
1285 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1286 out->headnode[j] = LittleLong(in->headnode[j]);
1287 out->visleafs = LittleLong(in->visleafs);
1288 out->firstface = LittleLong(in->firstface);
1289 out->numfaces = LittleLong(in->numfaces);
1293 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1299 in = (void *)(mod_base + l->fileofs);
1300 if (l->filelen % sizeof(*in))
1301 Host_Error("Mod_Q1BSP_LoadEdges: 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.edges = out;
1306 loadmodel->brushq1.numedges = count;
1308 for ( i=0 ; i<count ; i++, in++, out++)
1310 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1311 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1315 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1319 int i, j, k, count, miptex;
1321 in = (void *)(mod_base + l->fileofs);
1322 if (l->filelen % sizeof(*in))
1323 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1324 count = l->filelen / sizeof(*in);
1325 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1327 loadmodel->brushq1.texinfo = out;
1328 loadmodel->brushq1.numtexinfo = count;
1330 for (i = 0;i < count;i++, in++, out++)
1332 for (k = 0;k < 2;k++)
1333 for (j = 0;j < 4;j++)
1334 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1336 miptex = LittleLong(in->miptex);
1337 out->flags = LittleLong(in->flags);
1339 out->texture = NULL;
1340 if (loadmodel->brushq1.textures)
1342 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1343 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1345 out->texture = loadmodel->brushq1.textures + miptex;
1347 if (out->flags & TEX_SPECIAL)
1349 // if texture chosen is NULL or the shader needs a lightmap,
1350 // force to notexture water shader
1351 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1352 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1356 // if texture chosen is NULL, force to notexture
1357 if (out->texture == NULL)
1358 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1364 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1369 mins[0] = mins[1] = mins[2] = 9999;
1370 maxs[0] = maxs[1] = maxs[2] = -9999;
1372 for (i = 0;i < numverts;i++)
1374 for (j = 0;j < 3;j++, v++)
1384 #define MAX_SUBDIVPOLYTRIANGLES 4096
1385 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1387 static int subdivpolyverts, subdivpolytriangles;
1388 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1389 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1391 static int subdivpolylookupvert(vec3_t v)
1394 for (i = 0;i < subdivpolyverts;i++)
1395 if (subdivpolyvert[i][0] == v[0]
1396 && subdivpolyvert[i][1] == v[1]
1397 && subdivpolyvert[i][2] == v[2])
1399 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1400 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1401 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1402 return subdivpolyverts++;
1405 static void SubdividePolygon(int numverts, float *verts)
1407 int i, i1, i2, i3, f, b, c, p;
1408 vec3_t mins, maxs, front[256], back[256];
1409 float m, *pv, *cv, dist[256], frac;
1412 Host_Error("SubdividePolygon: ran out of verts in buffer");
1414 BoundPoly(numverts, verts, mins, maxs);
1416 for (i = 0;i < 3;i++)
1418 m = (mins[i] + maxs[i]) * 0.5;
1419 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1420 if (maxs[i] - m < 8)
1422 if (m - mins[i] < 8)
1426 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1427 dist[c] = cv[i] - m;
1430 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1434 VectorCopy(pv, front[f]);
1439 VectorCopy(pv, back[b]);
1442 if (dist[p] == 0 || dist[c] == 0)
1444 if ((dist[p] > 0) != (dist[c] > 0) )
1447 frac = dist[p] / (dist[p] - dist[c]);
1448 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1449 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1450 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1456 SubdividePolygon(f, front[0]);
1457 SubdividePolygon(b, back[0]);
1461 i1 = subdivpolylookupvert(verts);
1462 i2 = subdivpolylookupvert(verts + 3);
1463 for (i = 2;i < numverts;i++)
1465 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1467 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1471 i3 = subdivpolylookupvert(verts + i * 3);
1472 subdivpolyindex[subdivpolytriangles][0] = i1;
1473 subdivpolyindex[subdivpolytriangles][1] = i2;
1474 subdivpolyindex[subdivpolytriangles][2] = i3;
1476 subdivpolytriangles++;
1480 //Breaks a polygon up along axial 64 unit
1481 //boundaries so that turbulent and sky warps
1482 //can be done reasonably.
1483 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1489 subdivpolytriangles = 0;
1490 subdivpolyverts = 0;
1491 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1492 if (subdivpolytriangles < 1)
1493 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1495 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1496 mesh->num_vertices = subdivpolyverts;
1497 mesh->num_triangles = subdivpolytriangles;
1498 mesh->vertex = (surfvertex_t *)(mesh + 1);
1499 mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1500 memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1502 for (i = 0;i < mesh->num_triangles;i++)
1503 for (j = 0;j < 3;j++)
1504 mesh->index[i*3+j] = subdivpolyindex[i][j];
1506 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1508 VectorCopy(subdivpolyvert[i], v->v);
1509 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1510 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1515 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1518 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1519 mesh->num_vertices = numverts;
1520 mesh->num_triangles = numtriangles;
1521 mesh->data_vertex3f = (float *)(mesh + 1);
1522 mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1523 mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1524 mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1525 mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1526 mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1527 mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1528 mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1529 mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1530 mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1534 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1537 float *vec, *vert, mins[3], maxs[3], val, *v;
1540 // convert edges back to a normal polygon
1541 surf->poly_numverts = numedges;
1542 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1543 for (i = 0;i < numedges;i++)
1545 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1547 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1549 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1550 VectorCopy(vec, vert);
1554 // calculate polygon bounding box and center
1555 vert = surf->poly_verts;
1556 VectorCopy(vert, mins);
1557 VectorCopy(vert, maxs);
1559 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1561 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1562 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1563 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1565 VectorCopy(mins, surf->poly_mins);
1566 VectorCopy(maxs, surf->poly_maxs);
1567 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1568 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1569 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1571 // generate surface extents information
1572 tex = surf->texinfo;
1573 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1574 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1575 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1577 for (j = 0;j < 2;j++)
1579 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1586 for (i = 0;i < 2;i++)
1588 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1589 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1593 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1597 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1601 in = (void *)(mod_base + l->fileofs);
1602 if (l->filelen % sizeof(*in))
1603 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1604 count = l->filelen / sizeof(*in);
1605 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1607 loadmodel->brushq1.numsurfaces = count;
1608 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1609 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1610 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1612 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++)
1614 surf->number = surfnum;
1615 // FIXME: validate edges, texinfo, etc?
1616 firstedge = LittleLong(in->firstedge);
1617 numedges = LittleShort(in->numedges);
1618 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)
1619 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1620 i = LittleShort(in->texinfo);
1621 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1622 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1623 surf->texinfo = loadmodel->brushq1.texinfo + i;
1624 surf->flags = surf->texinfo->texture->flags;
1626 planenum = LittleShort(in->planenum);
1627 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1628 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1630 if (LittleShort(in->side))
1631 surf->flags |= SURF_PLANEBACK;
1633 surf->plane = loadmodel->brushq1.planes + planenum;
1635 // clear lightmap (filled in later)
1636 surf->lightmaptexture = NULL;
1638 // force lightmap upload on first time seeing the surface
1639 surf->cached_dlight = true;
1641 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1643 ssize = (surf->extents[0] >> 4) + 1;
1644 tsize = (surf->extents[1] >> 4) + 1;
1647 for (i = 0;i < MAXLIGHTMAPS;i++)
1648 surf->styles[i] = in->styles[i];
1649 i = LittleLong(in->lightofs);
1651 surf->samples = NULL;
1652 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1653 surf->samples = loadmodel->brushq1.lightdata + i;
1654 else // LordHavoc: white lighting (bsp version 29)
1655 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1657 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1659 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1660 Host_Error("Bad surface extents");
1661 // stainmap for permanent marks on walls
1662 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1664 memset(surf->stainsamples, 255, ssize * tsize * 3);
1668 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1670 for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++)
1673 mesh->num_vertices = surf->poly_numverts;
1674 mesh->num_triangles = surf->poly_numverts - 2;
1675 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1676 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1677 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1678 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1679 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1680 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1681 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1682 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1683 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1684 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1686 surf->lightmaptexturestride = 0;
1687 surf->lightmaptexture = NULL;
1689 for (i = 0;i < mesh->num_vertices;i++)
1691 mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1692 mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1693 mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1694 s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1695 t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1696 mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1697 mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1698 mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1699 mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1700 mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1701 mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1702 mesh->data_lightmapoffsets[i] = 0;
1705 for (i = 0;i < mesh->num_triangles;i++)
1707 mesh->data_element3i[i * 3 + 0] = 0;
1708 mesh->data_element3i[i * 3 + 1] = i + 1;
1709 mesh->data_element3i[i * 3 + 2] = i + 2;
1712 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1713 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);
1715 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1717 int i, iu, iv, smax, tmax;
1718 float u, v, ubase, vbase, uscale, vscale;
1720 smax = surf->extents[0] >> 4;
1721 tmax = surf->extents[1] >> 4;
1723 surf->flags |= SURF_LIGHTMAP;
1724 if (r_miplightmaps.integer)
1726 surf->lightmaptexturestride = smax+1;
1727 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1731 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1732 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1734 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1735 uscale = (uscale - ubase) / (smax + 1);
1736 vscale = (vscale - vbase) / (tmax + 1);
1738 for (i = 0;i < mesh->num_vertices;i++)
1740 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1741 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1742 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1743 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1744 // LordHavoc: calc lightmap data offset for vertex lighting to use
1747 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1753 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1755 node->parent = parent;
1756 if (node->contents < 0)
1758 Mod_Q1BSP_SetParent(node->children[0], node);
1759 Mod_Q1BSP_SetParent(node->children[1], node);
1762 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1768 in = (void *)(mod_base + l->fileofs);
1769 if (l->filelen % sizeof(*in))
1770 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1771 count = l->filelen / sizeof(*in);
1772 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1774 loadmodel->brushq1.nodes = out;
1775 loadmodel->brushq1.numnodes = count;
1777 for ( i=0 ; i<count ; i++, in++, out++)
1779 for (j=0 ; j<3 ; j++)
1781 out->mins[j] = LittleShort(in->mins[j]);
1782 out->maxs[j] = LittleShort(in->maxs[j]);
1785 p = LittleLong(in->planenum);
1786 out->plane = loadmodel->brushq1.planes + p;
1788 out->firstsurface = LittleShort(in->firstface);
1789 out->numsurfaces = LittleShort(in->numfaces);
1791 for (j=0 ; j<2 ; j++)
1793 p = LittleShort(in->children[j]);
1795 out->children[j] = loadmodel->brushq1.nodes + p;
1797 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1801 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1804 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1808 int i, j, count, p, pvschainbytes;
1811 in = (void *)(mod_base + l->fileofs);
1812 if (l->filelen % sizeof(*in))
1813 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1814 count = l->filelen / sizeof(*in);
1815 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1817 loadmodel->brushq1.leafs = out;
1818 loadmodel->brushq1.numleafs = count;
1819 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1820 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1822 for ( i=0 ; i<count ; i++, in++, out++)
1824 for (j=0 ; j<3 ; j++)
1826 out->mins[j] = LittleShort(in->mins[j]);
1827 out->maxs[j] = LittleShort(in->maxs[j]);
1830 // FIXME: this function could really benefit from some error checking
1832 out->contents = LittleLong(in->contents);
1834 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1835 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1836 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
1838 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid marksurface range %i:%i outside range %i:%i\n", out->firstmarksurface, out->firstmarksurface + out->nummarksurfaces, 0, loadmodel->brushq1.nummarksurfaces);
1839 out->firstmarksurface = NULL;
1840 out->nummarksurfaces = 0;
1844 memset(out->pvsdata, 0xFF, pvschainbytes);
1845 pvs += pvschainbytes;
1847 p = LittleLong(in->visofs);
1850 if (p >= loadmodel->brushq1.num_compressedpvs)
1851 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
1853 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1856 for (j = 0;j < 4;j++)
1857 out->ambient_sound_level[j] = in->ambient_level[j];
1859 // FIXME: Insert caustics here
1863 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1865 dclipnode_t *in, *out;
1869 in = (void *)(mod_base + l->fileofs);
1870 if (l->filelen % sizeof(*in))
1871 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1872 count = l->filelen / sizeof(*in);
1873 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1875 loadmodel->brushq1.clipnodes = out;
1876 loadmodel->brushq1.numclipnodes = count;
1878 if (loadmodel->brush.ishlbsp)
1880 hull = &loadmodel->brushq1.hulls[1];
1881 hull->clipnodes = out;
1882 hull->firstclipnode = 0;
1883 hull->lastclipnode = count-1;
1884 hull->planes = loadmodel->brushq1.planes;
1885 hull->clip_mins[0] = -16;
1886 hull->clip_mins[1] = -16;
1887 hull->clip_mins[2] = -36;
1888 hull->clip_maxs[0] = 16;
1889 hull->clip_maxs[1] = 16;
1890 hull->clip_maxs[2] = 36;
1891 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1893 hull = &loadmodel->brushq1.hulls[2];
1894 hull->clipnodes = out;
1895 hull->firstclipnode = 0;
1896 hull->lastclipnode = count-1;
1897 hull->planes = loadmodel->brushq1.planes;
1898 hull->clip_mins[0] = -32;
1899 hull->clip_mins[1] = -32;
1900 hull->clip_mins[2] = -32;
1901 hull->clip_maxs[0] = 32;
1902 hull->clip_maxs[1] = 32;
1903 hull->clip_maxs[2] = 32;
1904 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1906 hull = &loadmodel->brushq1.hulls[3];
1907 hull->clipnodes = out;
1908 hull->firstclipnode = 0;
1909 hull->lastclipnode = count-1;
1910 hull->planes = loadmodel->brushq1.planes;
1911 hull->clip_mins[0] = -16;
1912 hull->clip_mins[1] = -16;
1913 hull->clip_mins[2] = -18;
1914 hull->clip_maxs[0] = 16;
1915 hull->clip_maxs[1] = 16;
1916 hull->clip_maxs[2] = 18;
1917 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1921 hull = &loadmodel->brushq1.hulls[1];
1922 hull->clipnodes = out;
1923 hull->firstclipnode = 0;
1924 hull->lastclipnode = count-1;
1925 hull->planes = loadmodel->brushq1.planes;
1926 hull->clip_mins[0] = -16;
1927 hull->clip_mins[1] = -16;
1928 hull->clip_mins[2] = -24;
1929 hull->clip_maxs[0] = 16;
1930 hull->clip_maxs[1] = 16;
1931 hull->clip_maxs[2] = 32;
1932 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1934 hull = &loadmodel->brushq1.hulls[2];
1935 hull->clipnodes = out;
1936 hull->firstclipnode = 0;
1937 hull->lastclipnode = count-1;
1938 hull->planes = loadmodel->brushq1.planes;
1939 hull->clip_mins[0] = -32;
1940 hull->clip_mins[1] = -32;
1941 hull->clip_mins[2] = -24;
1942 hull->clip_maxs[0] = 32;
1943 hull->clip_maxs[1] = 32;
1944 hull->clip_maxs[2] = 64;
1945 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1948 for (i=0 ; i<count ; i++, out++, in++)
1950 out->planenum = LittleLong(in->planenum);
1951 out->children[0] = LittleShort(in->children[0]);
1952 out->children[1] = LittleShort(in->children[1]);
1953 if (out->children[0] >= count || out->children[1] >= count)
1954 Host_Error("Corrupt clipping hull(out of range child)\n");
1958 //Duplicate the drawing hull structure as a clipping hull
1959 static void Mod_Q1BSP_MakeHull0(void)
1966 hull = &loadmodel->brushq1.hulls[0];
1968 in = loadmodel->brushq1.nodes;
1969 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1971 hull->clipnodes = out;
1972 hull->firstclipnode = 0;
1973 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1974 hull->planes = loadmodel->brushq1.planes;
1976 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1978 out->planenum = in->plane - loadmodel->brushq1.planes;
1979 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1980 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1984 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1989 in = (void *)(mod_base + l->fileofs);
1990 if (l->filelen % sizeof(*in))
1991 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1992 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1993 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1995 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1997 j = (unsigned) LittleShort(in[i]);
1998 if (j >= loadmodel->brushq1.numsurfaces)
1999 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2000 loadmodel->brushq1.marksurfaces[i] = j;
2004 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2009 in = (void *)(mod_base + l->fileofs);
2010 if (l->filelen % sizeof(*in))
2011 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2012 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2013 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2015 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2016 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2020 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2026 in = (void *)(mod_base + l->fileofs);
2027 if (l->filelen % sizeof(*in))
2028 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2030 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2031 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2033 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2035 out->normal[0] = LittleFloat(in->normal[0]);
2036 out->normal[1] = LittleFloat(in->normal[1]);
2037 out->normal[2] = LittleFloat(in->normal[2]);
2038 out->dist = LittleFloat(in->dist);
2044 typedef struct portal_s
2047 mnode_t *nodes[2]; // [0] = front side of plane
2048 struct portal_s *next[2];
2050 struct portal_s *chain; // all portals are linked into a list
2054 static portal_t *portalchain;
2061 static portal_t *AllocPortal(void)
2064 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2065 p->chain = portalchain;
2070 static void FreePortal(portal_t *p)
2075 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2077 // calculate children first
2078 if (node->children[0]->contents >= 0)
2079 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2080 if (node->children[1]->contents >= 0)
2081 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2083 // make combined bounding box from children
2084 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2085 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2086 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2087 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2088 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2089 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2092 static void Mod_Q1BSP_FinalizePortals(void)
2094 int i, j, numportals, numpoints;
2095 portal_t *p, *pnext;
2098 mleaf_t *leaf, *endleaf;
2101 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2102 leaf = loadmodel->brushq1.leafs;
2103 endleaf = leaf + loadmodel->brushq1.numleafs;
2104 for (;leaf < endleaf;leaf++)
2106 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2107 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2114 for (i = 0;i < 2;i++)
2116 leaf = (mleaf_t *)p->nodes[i];
2118 for (j = 0;j < w->numpoints;j++)
2120 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2121 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2122 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2123 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2124 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2125 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2132 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2134 // tally up portal and point counts
2140 // note: this check must match the one below or it will usually corrupt memory
2141 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2142 if (p->winding && p->nodes[0] != p->nodes[1]
2143 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2144 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2147 numpoints += p->winding->numpoints * 2;
2151 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2152 loadmodel->brushq1.numportals = numportals;
2153 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2154 loadmodel->brushq1.numportalpoints = numpoints;
2155 // clear all leaf portal chains
2156 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2157 loadmodel->brushq1.leafs[i].portals = NULL;
2158 // process all portals in the global portal chain, while freeing them
2159 portal = loadmodel->brushq1.portals;
2160 point = loadmodel->brushq1.portalpoints;
2169 // note: this check must match the one above or it will usually corrupt memory
2170 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2171 if (p->nodes[0] != p->nodes[1]
2172 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2173 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2175 // first make the back to front portal(forward portal)
2176 portal->points = point;
2177 portal->numpoints = p->winding->numpoints;
2178 portal->plane.dist = p->plane.dist;
2179 VectorCopy(p->plane.normal, portal->plane.normal);
2180 portal->here = (mleaf_t *)p->nodes[1];
2181 portal->past = (mleaf_t *)p->nodes[0];
2183 for (j = 0;j < portal->numpoints;j++)
2185 VectorCopy(p->winding->points[j], point->position);
2188 PlaneClassify(&portal->plane);
2190 // link into leaf's portal chain
2191 portal->next = portal->here->portals;
2192 portal->here->portals = portal;
2194 // advance to next portal
2197 // then make the front to back portal(backward portal)
2198 portal->points = point;
2199 portal->numpoints = p->winding->numpoints;
2200 portal->plane.dist = -p->plane.dist;
2201 VectorNegate(p->plane.normal, portal->plane.normal);
2202 portal->here = (mleaf_t *)p->nodes[0];
2203 portal->past = (mleaf_t *)p->nodes[1];
2205 for (j = portal->numpoints - 1;j >= 0;j--)
2207 VectorCopy(p->winding->points[j], point->position);
2210 PlaneClassify(&portal->plane);
2212 // link into leaf's portal chain
2213 portal->next = portal->here->portals;
2214 portal->here->portals = portal;
2216 // advance to next portal
2219 Winding_Free(p->winding);
2231 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2234 Host_Error("AddPortalToNodes: NULL front node");
2236 Host_Error("AddPortalToNodes: NULL back node");
2237 if (p->nodes[0] || p->nodes[1])
2238 Host_Error("AddPortalToNodes: already included");
2239 // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
2241 p->nodes[0] = front;
2242 p->next[0] = (portal_t *)front->portals;
2243 front->portals = (mportal_t *)p;
2246 p->next[1] = (portal_t *)back->portals;
2247 back->portals = (mportal_t *)p;
2252 RemovePortalFromNode
2255 static void RemovePortalFromNodes(portal_t *portal)
2259 void **portalpointer;
2261 for (i = 0;i < 2;i++)
2263 node = portal->nodes[i];
2265 portalpointer = (void **) &node->portals;
2270 Host_Error("RemovePortalFromNodes: portal not in leaf");
2274 if (portal->nodes[0] == node)
2276 *portalpointer = portal->next[0];
2277 portal->nodes[0] = NULL;
2279 else if (portal->nodes[1] == node)
2281 *portalpointer = portal->next[1];
2282 portal->nodes[1] = NULL;
2285 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2289 if (t->nodes[0] == node)
2290 portalpointer = (void **) &t->next[0];
2291 else if (t->nodes[1] == node)
2292 portalpointer = (void **) &t->next[1];
2294 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2299 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2302 mnode_t *front, *back, *other_node;
2303 mplane_t clipplane, *plane;
2304 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2305 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2307 // if a leaf, we're done
2311 plane = node->plane;
2313 front = node->children[0];
2314 back = node->children[1];
2316 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2318 // create the new portal by generating a polygon for the node plane,
2319 // and clipping it by all of the other portals(which came from nodes above this one)
2320 nodeportal = AllocPortal();
2321 nodeportal->plane = *plane;
2323 nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2324 side = 0; // shut up compiler warning
2325 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2327 clipplane = portal->plane;
2328 if (portal->nodes[0] == portal->nodes[1])
2329 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2330 if (portal->nodes[0] == node)
2332 else if (portal->nodes[1] == node)
2334 clipplane.dist = -clipplane.dist;
2335 VectorNegate(clipplane.normal, clipplane.normal);
2339 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2341 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2342 if (!nodeportalwinding)
2344 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2349 if (nodeportalwinding)
2351 // if the plane was not clipped on all sides, there was an error
2352 nodeportal->winding = nodeportalwinding;
2353 AddPortalToNodes(nodeportal, front, back);
2356 // split the portals of this node along this node's plane and assign them to the children of this node
2357 // (migrating the portals downward through the tree)
2358 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2360 if (portal->nodes[0] == portal->nodes[1])
2361 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2362 if (portal->nodes[0] == node)
2364 else if (portal->nodes[1] == node)
2367 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2368 nextportal = portal->next[side];
2370 other_node = portal->nodes[!side];
2371 RemovePortalFromNodes(portal);
2373 // cut the portal into two portals, one on each side of the node plane
2374 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2379 AddPortalToNodes(portal, back, other_node);
2381 AddPortalToNodes(portal, other_node, back);
2387 AddPortalToNodes(portal, front, other_node);
2389 AddPortalToNodes(portal, other_node, front);
2393 // the winding is split
2394 splitportal = AllocPortal();
2395 temp = splitportal->chain;
2396 *splitportal = *portal;
2397 splitportal->chain = temp;
2398 splitportal->winding = backwinding;
2399 Winding_Free(portal->winding);
2400 portal->winding = frontwinding;
2404 AddPortalToNodes(portal, front, other_node);
2405 AddPortalToNodes(splitportal, back, other_node);
2409 AddPortalToNodes(portal, other_node, front);
2410 AddPortalToNodes(splitportal, other_node, back);
2414 Mod_Q1BSP_RecursiveNodePortals(front);
2415 Mod_Q1BSP_RecursiveNodePortals(back);
2418 static void Mod_Q1BSP_MakePortals(void)
2421 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2422 Mod_Q1BSP_FinalizePortals();
2425 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2428 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2429 msurface_t *surf, *s;
2430 float *v0, *v1, *v2, *v3;
2431 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2432 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2433 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2435 for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
2437 if (surf->neighborsurfaces[vertnum])
2439 surf->neighborsurfaces[vertnum] = NULL;
2440 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2442 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2443 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2444 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2447 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2448 if (s->neighborsurfaces[vnum] == surf)
2450 if (vnum < s->poly_numverts)
2452 for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
2454 if (s->neighborsurfaces[vnum] == NULL
2455 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2456 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2458 surf->neighborsurfaces[vertnum] = s;
2459 s->neighborsurfaces[vnum] = surf;
2463 if (vnum < s->poly_numverts)
2471 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2473 int i, j, stylecounts[256], totalcount, remapstyles[256];
2475 memset(stylecounts, 0, sizeof(stylecounts));
2476 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2478 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2479 for (j = 0;j < MAXLIGHTMAPS;j++)
2480 stylecounts[surf->styles[j]]++;
2483 model->brushq1.light_styles = 0;
2484 for (i = 0;i < 255;i++)
2488 remapstyles[i] = model->brushq1.light_styles++;
2489 totalcount += stylecounts[i] + 1;
2494 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2495 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2496 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2497 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2498 model->brushq1.light_styles = 0;
2499 for (i = 0;i < 255;i++)
2501 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2503 for (i = 0;i < model->brushq1.light_styles;i++)
2505 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2506 j += stylecounts[model->brushq1.light_style[i]] + 1;
2508 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2510 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2511 for (j = 0;j < MAXLIGHTMAPS;j++)
2512 if (surf->styles[j] != 255)
2513 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2516 for (i = 0;i < model->brushq1.light_styles;i++)
2518 *model->brushq1.light_styleupdatechains[i] = NULL;
2519 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2520 j += stylecounts[model->brushq1.light_style[i]] + 1;
2524 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2527 for (i = 0;i < model->brushq1.numtextures;i++)
2528 model->brushq1.pvstexturechainslength[i] = 0;
2529 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2531 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2533 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2534 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2537 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2539 if (model->brushq1.pvstexturechainslength[i])
2541 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2542 j += model->brushq1.pvstexturechainslength[i] + 1;
2545 model->brushq1.pvstexturechains[i] = NULL;
2547 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2548 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2549 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2550 for (i = 0;i < model->brushq1.numtextures;i++)
2552 if (model->brushq1.pvstexturechainslength[i])
2554 *model->brushq1.pvstexturechains[i] = NULL;
2555 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2560 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2565 while (node->contents >= 0)
2567 d = PlaneDiff(org, node->plane);
2569 node = node->children[0];
2570 else if (d < -radius)
2571 node = node->children[1];
2574 // go down both sides
2575 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2576 node = node->children[1];
2580 // if this is a leaf, accumulate the pvs bits
2581 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2582 for (i = 0;i < pvsbytes;i++)
2583 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2586 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2587 //of the given point.
2588 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2590 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2591 bytes = min(bytes, pvsbufferlength);
2592 memset(pvsbuffer, 0, bytes);
2593 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2597 //Returns PVS data for a given point
2598 //(note: always returns valid data, never NULL)
2599 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2602 Mod_CheckLoaded(model);
2603 // LordHavoc: modified to start at first clip node,
2604 // in other words: first node of the (sub)model
2605 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2606 while (node->contents == 0)
2607 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2608 return ((mleaf_t *)node)->pvsdata;
2611 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2616 VectorSubtract(inmaxs, inmins, size);
2617 if (cmodel->brush.ishlbsp)
2620 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2621 else if (size[0] <= 32)
2623 if (size[2] < 54) // pick the nearest of 36 or 72
2624 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2626 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2629 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2634 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2635 else if (size[0] <= 32)
2636 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2638 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2640 VectorCopy(inmins, outmins);
2641 VectorAdd(inmins, hull->clip_size, outmaxs);
2644 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2645 extern void R_Model_Brush_Draw(entity_render_t *ent);
2646 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2647 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
2648 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2653 mempool_t *mainmempool;
2655 model_t *originalloadmodel;
2656 float dist, modelyawradius, modelradius, *vec;
2659 mod->type = mod_brush;
2661 header = (dheader_t *)buffer;
2663 i = LittleLong(header->version);
2664 if (i != BSPVERSION && i != 30)
2665 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2666 mod->brush.ishlbsp = i == 30;
2668 mod->soundfromcenter = true;
2669 mod->TraceBox = Mod_Q1BSP_TraceBox;
2670 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2671 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2672 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2673 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2674 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2675 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2676 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2677 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2678 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2679 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2680 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2682 if (loadmodel->isworldmodel)
2684 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2685 // until we get a texture for it...
2689 // swap all the lumps
2690 mod_base = (qbyte *)header;
2692 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2693 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2697 // store which lightmap format to use
2698 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2700 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2701 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2702 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2703 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2704 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2705 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2706 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2707 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2708 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2709 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2710 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2711 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2712 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2713 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2714 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2716 if (mod->brushq1.data_compressedpvs)
2717 Mem_Free(mod->brushq1.data_compressedpvs);
2718 mod->brushq1.data_compressedpvs = NULL;
2719 mod->brushq1.num_compressedpvs = 0;
2721 Mod_Q1BSP_MakeHull0();
2722 Mod_Q1BSP_MakePortals();
2724 if (developer.integer)
2725 Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.numleafs - 1, loadmodel->brushq1.numportals);
2727 mod->numframes = 2; // regular and alternate animation
2729 mainmempool = mod->mempool;
2730 loadname = mod->name;
2732 Mod_Q1BSP_LoadLightList();
2733 originalloadmodel = loadmodel;
2736 // set up the submodels(FIXME: this is confusing)
2738 for (i = 0;i < mod->brush.numsubmodels;i++)
2740 bm = &mod->brushq1.submodels[i];
2742 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2743 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2745 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2746 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2749 mod->brushq1.firstmodelsurface = bm->firstface;
2750 mod->brushq1.nummodelsurfaces = bm->numfaces;
2752 // this gets altered below if sky is used
2753 mod->DrawSky = NULL;
2754 mod->Draw = R_Model_Brush_Draw;
2755 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2756 mod->DrawLight = R_Model_Brush_DrawLight;
2757 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2758 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2759 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2760 Mod_Q1BSP_BuildPVSTextureChains(mod);
2761 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2762 if (mod->brushq1.nummodelsurfaces)
2764 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2765 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2766 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2769 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2771 // we only need to have a drawsky function if it is used(usually only on world model)
2772 if (surf->texinfo->texture->shader == &Cshader_sky)
2773 mod->DrawSky = R_Model_Brush_DrawSky;
2774 // LordHavoc: submodels always clip, even if water
2775 if (mod->brush.numsubmodels - 1)
2776 surf->flags |= SURF_SOLIDCLIP;
2777 // calculate bounding shapes
2778 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
2780 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2781 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2782 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2783 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2784 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2785 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2786 dist = vec[0]*vec[0]+vec[1]*vec[1];
2787 if (modelyawradius < dist)
2788 modelyawradius = dist;
2789 dist += vec[2]*vec[2];
2790 if (modelradius < dist)
2794 modelyawradius = sqrt(modelyawradius);
2795 modelradius = sqrt(modelradius);
2796 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2797 mod->yawmins[2] = mod->normalmins[2];
2798 mod->yawmaxs[2] = mod->normalmaxs[2];
2799 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2800 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2801 mod->radius = modelradius;
2802 mod->radius2 = modelradius * modelradius;
2806 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2807 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2809 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2811 mod->brushq1.visleafs = bm->visleafs;
2813 // LordHavoc: only register submodels if it is the world
2814 // (prevents bsp models from replacing world submodels)
2815 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2818 // duplicate the basic information
2819 sprintf(name, "*%i", i+1);
2820 loadmodel = Mod_FindName(name);
2822 strcpy(loadmodel->name, name);
2823 // textures and memory belong to the main model
2824 loadmodel->texturepool = NULL;
2825 loadmodel->mempool = NULL;
2830 loadmodel = originalloadmodel;
2831 //Mod_Q1BSP_ProcessLightList();
2834 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2838 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2845 in = (void *)(mod_base + l->fileofs);
2846 if (l->filelen % sizeof(*in))
2847 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2848 count = l->filelen / sizeof(*in);
2849 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2852 loadmodel->num = count;
2854 for (i = 0;i < count;i++, in++, out++)
2860 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2867 in = (void *)(mod_base + l->fileofs);
2868 if (l->filelen % sizeof(*in))
2869 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2870 count = l->filelen / sizeof(*in);
2871 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2874 loadmodel->num = count;
2876 for (i = 0;i < count;i++, in++, out++)
2882 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2889 in = (void *)(mod_base + l->fileofs);
2890 if (l->filelen % sizeof(*in))
2891 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2892 count = l->filelen / sizeof(*in);
2893 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2896 loadmodel->num = count;
2898 for (i = 0;i < count;i++, in++, out++)
2904 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2911 in = (void *)(mod_base + l->fileofs);
2912 if (l->filelen % sizeof(*in))
2913 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2914 count = l->filelen / sizeof(*in);
2915 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2918 loadmodel->num = count;
2920 for (i = 0;i < count;i++, in++, out++)
2926 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2933 in = (void *)(mod_base + l->fileofs);
2934 if (l->filelen % sizeof(*in))
2935 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2936 count = l->filelen / sizeof(*in);
2937 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2940 loadmodel->num = count;
2942 for (i = 0;i < count;i++, in++, out++)
2948 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2955 in = (void *)(mod_base + l->fileofs);
2956 if (l->filelen % sizeof(*in))
2957 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2958 count = l->filelen / sizeof(*in);
2959 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2962 loadmodel->num = count;
2964 for (i = 0;i < count;i++, in++, out++)
2970 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2977 in = (void *)(mod_base + l->fileofs);
2978 if (l->filelen % sizeof(*in))
2979 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2980 count = l->filelen / sizeof(*in);
2981 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2984 loadmodel->num = count;
2986 for (i = 0;i < count;i++, in++, out++)
2992 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
2999 in = (void *)(mod_base + l->fileofs);
3000 if (l->filelen % sizeof(*in))
3001 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3002 count = l->filelen / sizeof(*in);
3003 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3006 loadmodel->num = count;
3008 for (i = 0;i < count;i++, in++, out++)
3014 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3021 in = (void *)(mod_base + l->fileofs);
3022 if (l->filelen % sizeof(*in))
3023 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3024 count = l->filelen / sizeof(*in);
3025 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3028 loadmodel->num = count;
3030 for (i = 0;i < count;i++, in++, out++)
3036 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3043 in = (void *)(mod_base + l->fileofs);
3044 if (l->filelen % sizeof(*in))
3045 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3046 count = l->filelen / sizeof(*in);
3047 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3050 loadmodel->num = count;
3052 for (i = 0;i < count;i++, in++, out++)
3058 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3065 in = (void *)(mod_base + l->fileofs);
3066 if (l->filelen % sizeof(*in))
3067 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3068 count = l->filelen / sizeof(*in);
3069 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3072 loadmodel->num = count;
3074 for (i = 0;i < count;i++, in++, out++)
3080 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3087 in = (void *)(mod_base + l->fileofs);
3088 if (l->filelen % sizeof(*in))
3089 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3090 count = l->filelen / sizeof(*in);
3091 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3094 loadmodel->num = count;
3096 for (i = 0;i < count;i++, in++, out++)
3102 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3109 in = (void *)(mod_base + l->fileofs);
3110 if (l->filelen % sizeof(*in))
3111 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3112 count = l->filelen / sizeof(*in);
3113 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3116 loadmodel->num = count;
3118 for (i = 0;i < count;i++, in++, out++)
3124 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3131 in = (void *)(mod_base + l->fileofs);
3132 if (l->filelen % sizeof(*in))
3133 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3134 count = l->filelen / sizeof(*in);
3135 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3138 loadmodel->num = count;
3140 for (i = 0;i < count;i++, in++, out++)
3146 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3153 in = (void *)(mod_base + l->fileofs);
3154 if (l->filelen % sizeof(*in))
3155 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3156 count = l->filelen / sizeof(*in);
3157 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3160 loadmodel->num = count;
3162 for (i = 0;i < count;i++, in++, out++)
3168 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3175 in = (void *)(mod_base + l->fileofs);
3176 if (l->filelen % sizeof(*in))
3177 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3178 count = l->filelen / sizeof(*in);
3179 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3182 loadmodel->num = count;
3184 for (i = 0;i < count;i++, in++, out++)
3190 static void Mod_Q2BSP_LoadModels(lump_t *l)
3197 in = (void *)(mod_base + l->fileofs);
3198 if (l->filelen % sizeof(*in))
3199 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3200 count = l->filelen / sizeof(*in);
3201 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3204 loadmodel->num = count;
3206 for (i = 0;i < count;i++, in++, out++)
3212 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3215 q2dheader_t *header;
3217 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3219 mod->type = mod_brushq2;
3221 header = (q2dheader_t *)buffer;
3223 i = LittleLong(header->version);
3224 if (i != Q2BSPVERSION)
3225 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3226 mod->brush.ishlbsp = false;
3227 if (loadmodel->isworldmodel)
3229 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3230 // until we get a texture for it...
3234 mod_base = (qbyte *)header;
3236 // swap all the lumps
3237 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3238 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3240 // store which lightmap format to use
3241 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3243 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3244 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3245 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3246 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3247 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3248 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3249 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3250 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3251 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3252 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3253 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3254 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3255 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3256 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3257 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3258 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3259 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3260 // LordHavoc: must go last because this makes the submodels
3261 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3264 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3265 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3267 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3270 char key[128], value[4096];
3272 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3273 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3274 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3277 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3278 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3279 data = loadmodel->brush.entities;
3280 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3281 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3285 if (!COM_ParseToken(&data, false))
3287 if (com_token[0] == '}')
3288 break; // end of worldspawn
3289 if (com_token[0] == '_')
3290 strcpy(key, com_token + 1);
3292 strcpy(key, com_token);
3293 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3294 key[strlen(key)-1] = 0;
3295 if (!COM_ParseToken(&data, false))
3297 strcpy(value, com_token);
3298 if (!strcmp("gridsize", key))
3300 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3301 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3307 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3316 char shadername[Q3PATHLENGTH];
3318 in = (void *)(mod_base + l->fileofs);
3319 if (l->filelen % sizeof(*in))
3320 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3321 count = l->filelen / sizeof(*in);
3322 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3324 loadmodel->brushq3.data_textures = out;
3325 loadmodel->brushq3.num_textures = count;
3327 for (i = 0;i < count;i++, in++, out++)
3330 strlcpy (out->name, in->name, sizeof (out->name));
3331 out->surfaceflags = LittleLong(in->surfaceflags);
3332 out->nativecontents = LittleLong(in->contents);
3333 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3334 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3335 out->surfaceparms = -1;
3338 // do a quick parse of shader files to get surfaceparms
3339 if ((search = FS_Search("scripts/*.shader", true, false)))
3341 for (i = 0;i < search->numfilenames;i++)
3343 if ((f = FS_LoadFile(search->filenames[i], false)))
3346 while (COM_ParseToken(&text, false))
3348 snprintf(shadername, sizeof(shadername), "%s", com_token);
3350 if (COM_ParseToken(&text, false) && !strcmp(com_token, "{"))
3352 while (COM_ParseToken(&text, false))
3354 if (!strcmp(com_token, "}"))
3356 else if (!strcmp(com_token, "{"))
3358 while (COM_ParseToken(&text, false))
3360 if (!strcmp(com_token, "}"))
3364 else if (!strcmp(com_token, "surfaceparm"))
3366 if (COM_ParseToken(&text, true) && strcmp(com_token, "\n"))
3368 if (!strcmp(com_token, "alphashadow"))
3369 flags |= SURFACEPARM_ALPHASHADOW;
3370 else if (!strcmp(com_token, "areaportal"))
3371 flags |= SURFACEPARM_AREAPORTAL;
3372 else if (!strcmp(com_token, "clusterportal"))
3373 flags |= SURFACEPARM_CLUSTERPORTAL;
3374 else if (!strcmp(com_token, "detail"))
3375 flags |= SURFACEPARM_DETAIL;
3376 else if (!strcmp(com_token, "donotenter"))
3377 flags |= SURFACEPARM_DONOTENTER;
3378 else if (!strcmp(com_token, "fog"))
3379 flags |= SURFACEPARM_FOG;
3380 else if (!strcmp(com_token, "lava"))
3381 flags |= SURFACEPARM_LAVA;
3382 else if (!strcmp(com_token, "lightfilter"))
3383 flags |= SURFACEPARM_LIGHTFILTER;
3384 else if (!strcmp(com_token, "metalsteps"))
3385 flags |= SURFACEPARM_METALSTEPS;
3386 else if (!strcmp(com_token, "nodamage"))
3387 flags |= SURFACEPARM_NODAMAGE;
3388 else if (!strcmp(com_token, "nodlight"))
3389 flags |= SURFACEPARM_NODLIGHT;
3390 else if (!strcmp(com_token, "nodraw"))
3391 flags |= SURFACEPARM_NODRAW;
3392 else if (!strcmp(com_token, "nodrop"))
3393 flags |= SURFACEPARM_NODROP;
3394 else if (!strcmp(com_token, "noimpact"))
3395 flags |= SURFACEPARM_NOIMPACT;
3396 else if (!strcmp(com_token, "nolightmap"))
3397 flags |= SURFACEPARM_NOLIGHTMAP;
3398 else if (!strcmp(com_token, "nomarks"))
3399 flags |= SURFACEPARM_NOMARKS;
3400 else if (!strcmp(com_token, "nomipmaps"))
3401 flags |= SURFACEPARM_NOMIPMAPS;
3402 else if (!strcmp(com_token, "nonsolid"))
3403 flags |= SURFACEPARM_NONSOLID;
3404 else if (!strcmp(com_token, "origin"))
3405 flags |= SURFACEPARM_ORIGIN;
3406 else if (!strcmp(com_token, "playerclip"))
3407 flags |= SURFACEPARM_PLAYERCLIP;
3408 else if (!strcmp(com_token, "sky"))
3409 flags |= SURFACEPARM_SKY;
3410 else if (!strcmp(com_token, "slick"))
3411 flags |= SURFACEPARM_SLICK;
3412 else if (!strcmp(com_token, "slime"))
3413 flags |= SURFACEPARM_SLIME;
3414 else if (!strcmp(com_token, "structural"))
3415 flags |= SURFACEPARM_STRUCTURAL;
3416 else if (!strcmp(com_token, "trans"))
3417 flags |= SURFACEPARM_TRANS;
3418 else if (!strcmp(com_token, "water"))
3419 flags |= SURFACEPARM_WATER;
3421 Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
3422 if (!COM_ParseToken(&text, true) || strcmp(com_token, "\n"))
3424 Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
3430 Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
3436 // look for linebreak or }
3437 while(COM_ParseToken(&text, true) && strcmp(com_token, "\n") && strcmp(com_token, "}"));
3438 // break out to top level if it was }
3439 if (!strcmp(com_token, "}"))
3443 // add shader to list (shadername and flags)
3444 // actually here we just poke into the texture settings
3445 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3446 if (!strcmp(out->name, shadername))
3447 out->surfaceparms = flags;
3451 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3462 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3464 if (out->surfaceparms == -1)
3467 Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3468 out->surfaceparms = 0;
3469 // these are defaults
3470 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3471 || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3472 out->surfaceparms |= SURFACEPARM_NODRAW;
3473 if (!strncmp(out->name, "textures/skies/", 15))
3474 out->surfaceparms |= SURFACEPARM_SKY;
3475 if (R_TextureHasAlpha(out->skin.base))
3476 out->surfaceparms |= SURFACEPARM_TRANS;
3479 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3482 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3488 in = (void *)(mod_base + l->fileofs);
3489 if (l->filelen % sizeof(*in))
3490 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3491 count = l->filelen / sizeof(*in);
3492 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3494 loadmodel->brushq3.data_planes = out;
3495 loadmodel->brushq3.num_planes = count;
3497 for (i = 0;i < count;i++, in++, out++)
3499 out->normal[0] = LittleLong(in->normal[0]);
3500 out->normal[1] = LittleLong(in->normal[1]);
3501 out->normal[2] = LittleLong(in->normal[2]);
3502 out->dist = LittleLong(in->dist);
3507 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3510 q3mbrushside_t *out;
3513 in = (void *)(mod_base + l->fileofs);
3514 if (l->filelen % sizeof(*in))
3515 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3516 count = l->filelen / sizeof(*in);
3517 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3519 loadmodel->brushq3.data_brushsides = out;
3520 loadmodel->brushq3.num_brushsides = count;
3522 for (i = 0;i < count;i++, in++, out++)
3524 n = LittleLong(in->planeindex);
3525 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3526 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3527 out->plane = loadmodel->brushq3.data_planes + n;
3528 n = LittleLong(in->textureindex);
3529 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3530 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3531 out->texture = loadmodel->brushq3.data_textures + n;
3535 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3539 int i, j, n, c, count, maxplanes;
3541 winding_t *temp1, *temp2;
3543 in = (void *)(mod_base + l->fileofs);
3544 if (l->filelen % sizeof(*in))
3545 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3546 count = l->filelen / sizeof(*in);
3547 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3549 loadmodel->brushq3.data_brushes = out;
3550 loadmodel->brushq3.num_brushes = count;
3552 temp1 = Winding_New(64);
3553 temp2 = Winding_New(64);
3558 for (i = 0;i < count;i++, in++, out++)
3560 n = LittleLong(in->firstbrushside);
3561 c = LittleLong(in->numbrushsides);
3562 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3563 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3564 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3565 out->numbrushsides = c;
3566 n = LittleLong(in->textureindex);
3567 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3568 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3569 out->texture = loadmodel->brushq3.data_textures + n;
3571 // make a list of mplane_t structs to construct a colbrush from
3572 if (maxplanes < out->numbrushsides)
3574 maxplanes = out->numbrushsides;
3577 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3579 for (j = 0;j < out->numbrushsides;j++)
3581 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3582 planes[j].dist = out->firstbrushside[j].plane->dist;
3584 // make the colbrush from the planes
3585 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
3589 Winding_Free(temp1);
3590 Winding_Free(temp2);
3593 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3599 in = (void *)(mod_base + l->fileofs);
3600 if (l->filelen % sizeof(*in))
3601 Host_Error("Mod_Q3BSP_LoadEffects: 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_effects = out;
3606 loadmodel->brushq3.num_effects = count;
3608 for (i = 0;i < count;i++, in++, out++)
3610 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3611 n = LittleLong(in->brushindex);
3612 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3613 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3614 out->brush = loadmodel->brushq3.data_brushes + n;
3615 out->unknown = LittleLong(in->unknown);
3619 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3624 in = (void *)(mod_base + l->fileofs);
3625 if (l->filelen % sizeof(*in))
3626 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3627 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3628 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3629 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3630 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3631 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3632 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3633 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3634 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3636 for (i = 0;i < count;i++, in++)
3638 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3639 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3640 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3641 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3642 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3643 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3644 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3645 // svector/tvector are calculated later in face loading
3646 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3647 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3648 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3649 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3650 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3651 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3652 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3653 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3654 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3655 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3656 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3657 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3658 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3662 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3668 in = (void *)(mod_base + l->fileofs);
3669 if (l->filelen % sizeof(int[3]))
3670 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3671 count = l->filelen / sizeof(*in);
3672 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3674 loadmodel->brushq3.num_triangles = count / 3;
3675 loadmodel->brushq3.data_element3i = out;
3676 loadmodel->brushq3.data_neighbor3i = out + count;
3678 for (i = 0;i < count;i++, in++, out++)
3680 *out = LittleLong(*in);
3681 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3683 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3689 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3697 in = (void *)(mod_base + l->fileofs);
3698 if (l->filelen % sizeof(*in))
3699 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3700 count = l->filelen / sizeof(*in);
3701 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3703 loadmodel->brushq3.data_lightmaps = out;
3704 loadmodel->brushq3.num_lightmaps = count;
3706 for (i = 0;i < count;i++, in++, out++)
3707 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3710 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3714 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3715 //int *originalelement3i;
3716 //int *originalneighbor3i;
3717 float *originalvertex3f;
3718 //float *originalsvector3f;
3719 //float *originaltvector3f;
3720 //float *originalnormal3f;
3721 float *originalcolor4f;
3722 float *originaltexcoordtexture2f;
3723 float *originaltexcoordlightmap2f;
3726 in = (void *)(mod_base + l->fileofs);
3727 if (l->filelen % sizeof(*in))
3728 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3729 count = l->filelen / sizeof(*in);
3730 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3732 loadmodel->brushq3.data_faces = out;
3733 loadmodel->brushq3.num_faces = count;
3735 for (i = 0;i < count;i++, in++, out++)
3737 // check face type first
3738 out->type = LittleLong(in->type);
3739 if (out->type != Q3FACETYPE_POLYGON
3740 && out->type != Q3FACETYPE_PATCH
3741 && out->type != Q3FACETYPE_MESH
3742 && out->type != Q3FACETYPE_FLARE)
3744 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3745 out->num_vertices = 0;
3746 out->num_triangles = 0;
3747 out->type = 0; // error
3751 n = LittleLong(in->textureindex);
3752 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3754 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3755 out->num_vertices = 0;
3756 out->num_triangles = 0;
3757 out->type = 0; // error
3761 out->texture = loadmodel->brushq3.data_textures + n;
3762 n = LittleLong(in->effectindex);
3763 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3765 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3771 out->effect = loadmodel->brushq3.data_effects + n;
3772 n = LittleLong(in->lightmapindex);
3773 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3775 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3779 out->lightmaptexture = NULL;
3781 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3783 out->firstvertex = LittleLong(in->firstvertex);
3784 out->num_vertices = LittleLong(in->numvertices);
3785 out->firstelement = LittleLong(in->firstelement);
3786 out->num_triangles = LittleLong(in->numelements) / 3;
3787 if (out->num_triangles * 3 != LittleLong(in->numelements))
3789 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));
3790 out->num_vertices = 0;
3791 out->num_triangles = 0;
3792 out->type = 0; // error
3795 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3797 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);
3798 out->num_vertices = 0;
3799 out->num_triangles = 0;
3800 out->type = 0; // error
3803 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3805 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);
3806 out->num_vertices = 0;
3807 out->num_triangles = 0;
3808 out->type = 0; // error
3813 case Q3FACETYPE_POLYGON:
3814 case Q3FACETYPE_MESH:
3815 // no processing necessary
3816 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3817 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3818 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3819 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3820 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3821 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3822 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3823 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3824 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3826 case Q3FACETYPE_PATCH:
3827 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3828 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3829 if (patchsize[0] < 1 || patchsize[1] < 1)
3831 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3832 out->num_vertices = 0;
3833 out->num_triangles = 0;
3834 out->type = 0; // error
3837 // convert patch to Q3FACETYPE_MESH
3838 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3839 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3840 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3841 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3842 finalvertices = finalwidth * finalheight;
3843 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3844 originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3845 //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3846 //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3847 //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3848 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3849 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3850 originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3851 //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
3852 //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3854 originalvertex3f = out->data_vertex3f;
3855 //originalsvector3f = out->data_svector3f;
3856 //originaltvector3f = out->data_tvector3f;
3857 //originalnormal3f = out->data_normal3f;
3858 originalcolor4f = out->data_color4f;
3859 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3860 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3861 //originalelement3i = out->data_element3i;
3862 //originalneighbor3i = out->data_neighbor3i;
3864 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
3865 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3866 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3867 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3868 out->data_color4f = out->data_normal3f + finalvertices * 3;
3869 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3870 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3871 out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
3872 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3873 out->type = Q3FACETYPE_MESH;
3874 out->firstvertex = -1;
3875 out->num_vertices = finalvertices;
3876 out->firstelement = -1;
3877 out->num_triangles = finaltriangles;
3878 // generate geometry
3879 // (note: normals are skipped because they get recalculated)
3880 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3881 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3882 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3883 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3884 // generate elements
3885 e = out->data_element3i;
3886 for (y = 0;y < finalheight - 1;y++)
3888 row0 = (y + 0) * finalwidth;
3889 row1 = (y + 1) * finalwidth;
3890 for (x = 0;x < finalwidth - 1;x++)
3902 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3903 if (developer.integer)
3905 if (out->num_triangles < finaltriangles)
3906 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);
3908 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);
3910 // q3map does not put in collision brushes for curves... ugh
3911 out->collisions = true;
3913 case Q3FACETYPE_FLARE:
3914 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3916 out->num_vertices = 0;
3917 out->num_triangles = 0;
3921 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
3922 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3924 if (invalidelements)
3926 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);
3927 for (j = 0;j < out->num_triangles * 3;j++)
3929 Con_Printf(" %i", out->data_element3i[j]);
3930 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3931 out->data_element3i[j] = 0;
3935 // for shadow volumes
3936 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
3937 // for per pixel lighting
3938 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);
3939 // calculate a bounding box
3940 VectorClear(out->mins);
3941 VectorClear(out->maxs);
3942 if (out->num_vertices)
3944 VectorCopy(out->data_vertex3f, out->mins);
3945 VectorCopy(out->data_vertex3f, out->maxs);
3946 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
3948 out->mins[0] = min(out->mins[0], v[0]);
3949 out->maxs[0] = max(out->maxs[0], v[0]);
3950 out->mins[1] = min(out->mins[1], v[1]);
3951 out->maxs[1] = max(out->maxs[1], v[1]);
3952 out->mins[2] = min(out->mins[2], v[2]);
3953 out->maxs[2] = max(out->maxs[2], v[2]);
3955 out->mins[0] -= 1.0f;
3956 out->mins[1] -= 1.0f;
3957 out->mins[2] -= 1.0f;
3958 out->maxs[0] += 1.0f;
3959 out->maxs[1] += 1.0f;
3960 out->maxs[2] += 1.0f;
3964 // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
3967 int totalverts, totaltris;
3968 int originalnum_vertices;
3969 float *originaldata_vertex3f;
3970 float *originaldata_texcoordtexture2f;
3971 float *originaldata_texcoordlightmap2f;
3972 float *originaldata_svector3f;
3973 float *originaldata_tvector3f;
3974 float *originaldata_normal3f;
3975 float *originaldata_color4f;
3976 int originalnum_triangles;
3977 int *originaldata_element3i;
3978 int *originaldata_neighbor3i;
3982 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
3986 totalverts += out->num_vertices;
3987 totaltris += out->num_triangles;
3990 originalnum_vertices = loadmodel->brushq3.num_vertices;
3991 originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
3992 originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
3993 originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
3994 originaldata_svector3f = loadmodel->brushq3.data_svector3f;
3995 originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
3996 originaldata_normal3f = loadmodel->brushq3.data_normal3f;
3997 originaldata_color4f = loadmodel->brushq3.data_color4f;
3998 originalnum_triangles = loadmodel->brushq3.num_triangles;
3999 originaldata_element3i = loadmodel->brushq3.data_element3i;
4000 originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
4001 loadmodel->brushq3.num_vertices = totalverts;
4002 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
4003 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
4004 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
4005 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
4006 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
4007 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
4008 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
4009 loadmodel->brushq3.num_triangles = totaltris;
4010 loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
4011 loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
4014 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4018 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
4019 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
4020 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
4021 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
4022 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
4023 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
4024 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
4025 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
4026 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
4027 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
4028 if (out->firstvertex == -1)
4029 Mem_Free(out->data_vertex3f);
4030 if (out->firstelement == -1)
4031 Mem_Free(out->data_element3i);
4032 out->firstvertex = totalverts;
4033 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4034 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4035 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4036 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4037 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4038 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4039 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4040 out->firstelement = totaltris * 3;
4041 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4042 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4043 //for (j = 0;j < out->numtriangles * 3;j++)
4044 // out->data_element3i[j] += totalverts - out->firstvertex;
4045 totalverts += out->num_vertices;
4046 totaltris += out->num_triangles;
4048 Mem_Free(originaldata_vertex3f);
4049 Mem_Free(originaldata_element3i);
4054 static void Mod_Q3BSP_LoadModels(lump_t *l)
4058 int i, j, n, c, count;
4060 in = (void *)(mod_base + l->fileofs);
4061 if (l->filelen % sizeof(*in))
4062 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4063 count = l->filelen / sizeof(*in);
4064 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4066 loadmodel->brushq3.data_models = out;
4067 loadmodel->brushq3.num_models = count;
4069 for (i = 0;i < count;i++, in++, out++)
4071 for (j = 0;j < 3;j++)
4073 out->mins[j] = LittleFloat(in->mins[j]);
4074 out->maxs[j] = LittleFloat(in->maxs[j]);
4076 n = LittleLong(in->firstface);
4077 c = LittleLong(in->numfaces);
4078 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4079 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4080 out->firstface = loadmodel->brushq3.data_faces + n;
4082 n = LittleLong(in->firstbrush);
4083 c = LittleLong(in->numbrushes);
4084 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4085 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4086 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4087 out->numbrushes = c;
4091 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4097 in = (void *)(mod_base + l->fileofs);
4098 if (l->filelen % sizeof(*in))
4099 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4100 count = l->filelen / sizeof(*in);
4101 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4103 loadmodel->brushq3.data_leafbrushes = out;
4104 loadmodel->brushq3.num_leafbrushes = count;
4106 for (i = 0;i < count;i++, in++, out++)
4108 n = LittleLong(*in);
4109 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4110 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4111 *out = loadmodel->brushq3.data_brushes + n;
4115 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4121 in = (void *)(mod_base + l->fileofs);
4122 if (l->filelen % sizeof(*in))
4123 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4124 count = l->filelen / sizeof(*in);
4125 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4127 loadmodel->brushq3.data_leaffaces = out;
4128 loadmodel->brushq3.num_leaffaces = count;
4130 for (i = 0;i < count;i++, in++, out++)
4132 n = LittleLong(*in);
4133 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4134 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4135 *out = loadmodel->brushq3.data_faces + n;
4139 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4143 int i, j, n, c, count;
4145 in = (void *)(mod_base + l->fileofs);
4146 if (l->filelen % sizeof(*in))
4147 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4148 count = l->filelen / sizeof(*in);
4149 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4151 loadmodel->brushq3.data_leafs = out;
4152 loadmodel->brushq3.num_leafs = count;
4154 for (i = 0;i < count;i++, in++, out++)
4156 out->isnode = false;
4158 out->clusterindex = LittleLong(in->clusterindex);
4159 out->areaindex = LittleLong(in->areaindex);
4160 for (j = 0;j < 3;j++)
4162 // yes the mins/maxs are ints
4163 out->mins[j] = LittleLong(in->mins[j]);
4164 out->maxs[j] = LittleLong(in->maxs[j]);
4166 n = LittleLong(in->firstleafface);
4167 c = LittleLong(in->numleaffaces);
4168 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4169 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4170 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4171 out->numleaffaces = c;
4172 n = LittleLong(in->firstleafbrush);
4173 c = LittleLong(in->numleafbrushes);
4174 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4175 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4176 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4177 out->numleafbrushes = c;
4181 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4184 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4185 node->parent = parent;
4188 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4189 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4193 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4199 in = (void *)(mod_base + l->fileofs);
4200 if (l->filelen % sizeof(*in))
4201 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4202 count = l->filelen / sizeof(*in);
4203 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4205 loadmodel->brushq3.data_nodes = out;
4206 loadmodel->brushq3.num_nodes = count;
4208 for (i = 0;i < count;i++, in++, out++)
4212 n = LittleLong(in->planeindex);
4213 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4214 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4215 out->plane = loadmodel->brushq3.data_planes + n;
4216 for (j = 0;j < 2;j++)
4218 n = LittleLong(in->childrenindex[j]);
4221 if (n >= loadmodel->brushq3.num_nodes)
4222 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4223 out->children[j] = loadmodel->brushq3.data_nodes + n;
4228 if (n >= loadmodel->brushq3.num_leafs)
4229 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4230 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4233 for (j = 0;j < 3;j++)
4235 // yes the mins/maxs are ints
4236 out->mins[j] = LittleLong(in->mins[j]);
4237 out->maxs[j] = LittleLong(in->maxs[j]);
4241 // set the parent pointers
4242 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4245 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4248 q3dlightgrid_t *out;
4251 if (l->filelen == 0)
4254 in = (void *)(mod_base + l->fileofs);
4255 if (l->filelen % sizeof(*in))
4256 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4257 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4258 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4259 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4260 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4261 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4262 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4263 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4264 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4265 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4266 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4267 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4268 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4269 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4270 if (l->filelen < count * (int)sizeof(*in))
4271 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]);
4272 if (l->filelen != count * (int)sizeof(*in))
4273 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4275 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4276 loadmodel->brushq3.data_lightgrid = out;
4277 loadmodel->brushq3.num_lightgrid = count;
4279 // no swapping or validation necessary
4280 memcpy(out, in, count * (int)sizeof(*out));
4282 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]);
4283 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]);
4286 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4291 if (l->filelen == 0)
4294 in = (void *)(mod_base + l->fileofs);
4296 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4298 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4299 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4300 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4301 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4302 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4303 if (l->filelen < totalchains + (int)sizeof(*in))
4304 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);
4306 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4307 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4310 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4312 // FIXME: finish this code
4313 VectorCopy(in, out);
4316 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4318 int i, j, k, index[3];
4319 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4320 q3dlightgrid_t *a, *s;
4321 // FIXME: write this
4322 if (!model->brushq3.num_lightgrid)
4324 ambientcolor[0] = 1;
4325 ambientcolor[1] = 1;
4326 ambientcolor[2] = 1;
4329 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4330 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4331 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4332 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4333 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4334 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4335 index[0] = (int)floor(transformed[0]);
4336 index[1] = (int)floor(transformed[1]);
4337 index[2] = (int)floor(transformed[2]);
4338 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4339 // now lerp the values
4340 VectorClear(diffusenormal);
4341 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4342 for (k = 0;k < 2;k++)
4344 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4345 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4347 for (j = 0;j < 2;j++)
4349 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4350 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4352 for (i = 0;i < 2;i++)
4354 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4355 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4357 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4358 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4359 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4360 pitch = s->diffusepitch * M_PI / 128;
4361 yaw = s->diffuseyaw * M_PI / 128;
4362 sinpitch = sin(pitch);
4363 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4364 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4365 diffusenormal[2] += blend * (cos(pitch));
4366 //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)));
4370 VectorNormalize(diffusenormal);
4371 //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]);
4374 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)
4376 int i, startside, endside;
4377 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4381 if (startfrac >= trace->fraction)
4383 // note: all line fragments past first impact fraction are ignored
4384 while (node->isnode)
4386 // recurse down node sides
4387 dist1 = PlaneDiff(start, node->plane);
4388 dist2 = PlaneDiff(end, node->plane);
4389 startside = dist1 < 0;
4390 endside = dist2 < 0;
4391 if (startside == endside)
4393 // most of the time the line fragment is on one side of the plane
4394 node = node->children[startside];
4398 // line crosses node plane, split the line
4399 midfrac = dist1 / (dist1 - dist2);
4400 VectorLerp(linestart, midfrac, lineend, mid);
4401 // take the near side first
4402 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4403 if (midfrac < trace->fraction)
4404 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4409 segmentmins[0] = min(start[0], end[0]);
4410 segmentmins[1] = min(start[1], end[1]);
4411 segmentmins[2] = min(start[2], end[2]);
4412 segmentmaxs[0] = max(start[0], end[0]);
4413 segmentmaxs[1] = max(start[1], end[1]);
4414 segmentmaxs[2] = max(start[2], end[2]);
4415 leaf = (q3mleaf_t *)node;
4416 for (i = 0;i < leaf->numleafbrushes;i++)
4418 if (startfrac >= trace->fraction)
4420 brush = leaf->firstleafbrush[i]->colbrushf;
4421 if (brush && brush->markframe != markframe)
4423 brush->markframe = markframe;
4424 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4425 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4428 if (mod_q3bsp_curves_collisions.integer)
4430 for (i = 0;i < leaf->numleaffaces;i++)
4432 if (startfrac >= trace->fraction)
4434 face = leaf->firstleafface[i];
4435 if (face->collisions && face->collisionmarkframe != markframe)
4437 face->collisionmarkframe = markframe;
4438 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4439 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4445 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)
4448 float nodesegmentmins[3], nodesegmentmaxs[3];
4452 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4453 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4454 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4455 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4456 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4457 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4458 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4462 // recurse down node sides
4463 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4466 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4467 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4469 else if (sides == 2)
4470 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4472 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4474 dist = node->plane->dist - (1.0f / 8.0f);
4475 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4477 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4479 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4485 dist = node->plane->dist + (1.0f / 8.0f);
4486 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4488 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4490 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4496 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4498 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4500 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4506 leaf = (q3mleaf_t *)node;
4507 for (i = 0;i < leaf->numleafbrushes;i++)
4509 brush = leaf->firstleafbrush[i]->colbrushf;
4510 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4512 brush->markframe = markframe;
4513 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4516 if (mod_q3bsp_curves_collisions.integer)
4518 for (i = 0;i < leaf->numleaffaces;i++)
4520 face = leaf->firstleafface[i];
4521 // 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
4522 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4523 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4529 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)
4532 float segmentmins[3], segmentmaxs[3];
4533 colbrushf_t *thisbrush_start, *thisbrush_end;
4534 matrix4x4_t startmatrix, endmatrix;
4535 static int markframe = 0;
4537 memset(trace, 0, sizeof(*trace));
4538 trace->fraction = 1;
4539 trace->hitsupercontentsmask = hitsupercontentsmask;
4540 Matrix4x4_CreateIdentity(&startmatrix);
4541 Matrix4x4_CreateIdentity(&endmatrix);
4542 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4543 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4544 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4545 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4546 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4547 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4548 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4551 if (model->brushq3.submodel)
4553 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4554 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4555 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4556 if (mod_q3bsp_curves_collisions.integer)
4558 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4560 face = model->brushq3.data_thismodel->firstface + i;
4561 if (face->collisions)
4562 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4567 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4571 // box trace, performed as brush trace
4572 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4573 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4574 if (model->brushq3.submodel)
4576 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4577 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4578 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4579 if (mod_q3bsp_curves_collisions.integer)
4581 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4583 face = model->brushq3.data_thismodel->firstface + i;
4584 if (face->collisions)
4585 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4590 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4595 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)
4602 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4603 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4606 // node - recurse down the BSP tree
4607 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4610 node = node->children[0];
4613 node = node->children[1];
4615 default: // crossing
4616 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4618 node = node->children[1];
4625 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4627 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4630 //Returns PVS data for a given point
4631 //(note: can return NULL)
4632 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4635 Mod_CheckLoaded(model);
4636 node = model->brushq3.data_nodes;
4637 while (node->isnode)
4638 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4639 if (((q3mleaf_t *)node)->clusterindex >= 0)
4640 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4645 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4651 while (node->isnode)
4653 d = PlaneDiff(org, node->plane);
4655 node = node->children[0];
4656 else if (d < -radius)
4657 node = node->children[1];
4660 // go down both sides
4661 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4662 node = node->children[1];
4665 // if this is a leaf with a pvs, accumulate the pvs bits
4666 if (((q3mleaf_t *)node)->clusterindex >= 0)
4668 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4669 for (i = 0;i < pvsbytes;i++)
4670 pvsbuffer[i] |= pvs[i];
4675 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4676 //of the given point.
4677 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4679 int bytes = model->brushq3.num_pvschainlength;
4680 bytes = min(bytes, pvsbufferlength);
4681 memset(pvsbuffer, 0, bytes);
4682 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4687 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4689 int supercontents = 0;
4690 if (nativecontents & Q2CONTENTS_SOLID)
4691 supercontents |= SUPERCONTENTS_SOLID;
4692 if (nativecontents & Q2CONTENTS_WATER)
4693 supercontents |= SUPERCONTENTS_WATER;
4694 if (nativecontents & Q2CONTENTS_SLIME)
4695 supercontents |= SUPERCONTENTS_SLIME;
4696 if (nativecontents & Q2CONTENTS_LAVA)
4697 supercontents |= SUPERCONTENTS_LAVA;
4698 return supercontents;
4701 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4703 int nativecontents = 0;
4704 if (supercontents & SUPERCONTENTS_SOLID)
4705 nativecontents |= Q2CONTENTS_SOLID;
4706 if (supercontents & SUPERCONTENTS_WATER)
4707 nativecontents |= Q2CONTENTS_WATER;
4708 if (supercontents & SUPERCONTENTS_SLIME)
4709 nativecontents |= Q2CONTENTS_SLIME;
4710 if (supercontents & SUPERCONTENTS_LAVA)
4711 nativecontents |= Q2CONTENTS_LAVA;
4712 return nativecontents;
4715 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4716 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4717 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4718 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);
4719 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4722 q3dheader_t *header;
4723 float corner[3], yawradius, modelradius;
4725 mod->type = mod_brushq3;
4729 header = (q3dheader_t *)buffer;
4731 i = LittleLong(header->version);
4732 if (i != Q3BSPVERSION)
4733 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4734 if (loadmodel->isworldmodel)
4736 Cvar_SetValue("halflifebsp", false);
4737 // until we get a texture for it...
4741 mod->soundfromcenter = true;
4742 mod->TraceBox = Mod_Q3BSP_TraceBox;
4743 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4744 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4745 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4746 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4747 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4748 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4749 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4750 //mod->DrawSky = R_Q3BSP_DrawSky;
4751 mod->Draw = R_Q3BSP_Draw;
4752 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4753 mod->DrawLight = R_Q3BSP_DrawLight;
4755 mod_base = (qbyte *)header;
4757 // swap all the lumps
4758 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4759 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4761 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4762 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4763 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4764 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4765 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4766 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4767 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4768 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4769 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4770 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4771 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4772 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4773 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4774 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4775 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4776 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4777 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4778 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4780 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4787 // LordHavoc: only register submodels if it is the world
4788 // (prevents bsp models from replacing world submodels)
4789 if (!loadmodel->isworldmodel)
4791 // duplicate the basic information
4792 sprintf(name, "*%i", i);
4793 mod = Mod_FindName(name);
4795 strcpy(mod->name, name);
4796 // textures and memory belong to the main model
4797 mod->texturepool = NULL;
4798 mod->mempool = NULL;
4800 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4801 mod->brushq3.submodel = i;
4803 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4804 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4805 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4806 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4807 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4808 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4809 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4810 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4811 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4812 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4813 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4814 mod->yawmins[2] = mod->normalmins[2];
4815 mod->yawmaxs[2] = mod->normalmaxs[2];
4816 mod->radius = modelradius;
4817 mod->radius2 = modelradius * modelradius;
4819 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
4820 if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceparms & SURFACEPARM_SKY)
4822 if (j < mod->brushq3.data_thismodel->numfaces)
4823 mod->DrawSky = R_Q3BSP_DrawSky;
4827 void Mod_IBSP_Load(model_t *mod, void *buffer)
4829 int i = LittleLong(((int *)buffer)[1]);
4830 if (i == Q3BSPVERSION)
4831 Mod_Q3BSP_Load(mod,buffer);
4832 else if (i == Q2BSPVERSION)
4833 Mod_Q2BSP_Load(mod,buffer);
4835 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4838 void Mod_MAP_Load(model_t *mod, void *buffer)
4840 Host_Error("Mod_MAP_Load: not yet implemented\n");