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)
3313 in = (void *)(mod_base + l->fileofs);
3314 if (l->filelen % sizeof(*in))
3315 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3316 count = l->filelen / sizeof(*in);
3317 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3319 loadmodel->brushq3.data_textures = out;
3320 loadmodel->brushq3.num_textures = count;
3322 // FIXME: do a quick parse of the shader files to see if any names match
3323 // up and get their surfaceparms
3326 for (i = 0;i < count;i++, in++, out++)
3329 strlcpy (out->name, in->name, sizeof (out->name));
3330 out->surfaceflags = LittleLong(in->surfaceflags);
3331 out->nativecontents = LittleLong(in->contents);
3332 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3333 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3334 out->renderflags = 0;
3335 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3336 || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3337 out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3338 if (!strncmp(out->name, "textures/skies/", 15))
3339 out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY;
3340 if (R_TextureHasAlpha(out->skin.base))
3341 out->renderflags |= Q3MTEXTURERENDERFLAGS_TRANSPARENT;
3345 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3351 in = (void *)(mod_base + l->fileofs);
3352 if (l->filelen % sizeof(*in))
3353 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3354 count = l->filelen / sizeof(*in);
3355 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3357 loadmodel->brushq3.data_planes = out;
3358 loadmodel->brushq3.num_planes = count;
3360 for (i = 0;i < count;i++, in++, out++)
3362 out->normal[0] = LittleLong(in->normal[0]);
3363 out->normal[1] = LittleLong(in->normal[1]);
3364 out->normal[2] = LittleLong(in->normal[2]);
3365 out->dist = LittleLong(in->dist);
3370 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3373 q3mbrushside_t *out;
3376 in = (void *)(mod_base + l->fileofs);
3377 if (l->filelen % sizeof(*in))
3378 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3379 count = l->filelen / sizeof(*in);
3380 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3382 loadmodel->brushq3.data_brushsides = out;
3383 loadmodel->brushq3.num_brushsides = count;
3385 for (i = 0;i < count;i++, in++, out++)
3387 n = LittleLong(in->planeindex);
3388 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3389 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3390 out->plane = loadmodel->brushq3.data_planes + n;
3391 n = LittleLong(in->textureindex);
3392 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3393 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3394 out->texture = loadmodel->brushq3.data_textures + n;
3398 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3402 int i, j, n, c, count, maxplanes;
3404 winding_t *temp1, *temp2;
3406 in = (void *)(mod_base + l->fileofs);
3407 if (l->filelen % sizeof(*in))
3408 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3409 count = l->filelen / sizeof(*in);
3410 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3412 loadmodel->brushq3.data_brushes = out;
3413 loadmodel->brushq3.num_brushes = count;
3415 temp1 = Winding_New(64);
3416 temp2 = Winding_New(64);
3421 for (i = 0;i < count;i++, in++, out++)
3423 n = LittleLong(in->firstbrushside);
3424 c = LittleLong(in->numbrushsides);
3425 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3426 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3427 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3428 out->numbrushsides = c;
3429 n = LittleLong(in->textureindex);
3430 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3431 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3432 out->texture = loadmodel->brushq3.data_textures + n;
3434 // make a list of mplane_t structs to construct a colbrush from
3435 if (maxplanes < out->numbrushsides)
3437 maxplanes = out->numbrushsides;
3440 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3442 for (j = 0;j < out->numbrushsides;j++)
3444 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3445 planes[j].dist = out->firstbrushside[j].plane->dist;
3447 // make the colbrush from the planes
3448 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
3452 Winding_Free(temp1);
3453 Winding_Free(temp2);
3456 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3462 in = (void *)(mod_base + l->fileofs);
3463 if (l->filelen % sizeof(*in))
3464 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3465 count = l->filelen / sizeof(*in);
3466 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3468 loadmodel->brushq3.data_effects = out;
3469 loadmodel->brushq3.num_effects = count;
3471 for (i = 0;i < count;i++, in++, out++)
3473 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3474 n = LittleLong(in->brushindex);
3475 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3476 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3477 out->brush = loadmodel->brushq3.data_brushes + n;
3478 out->unknown = LittleLong(in->unknown);
3482 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3487 in = (void *)(mod_base + l->fileofs);
3488 if (l->filelen % sizeof(*in))
3489 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3490 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3491 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3492 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3493 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3494 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3495 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3496 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3497 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3499 for (i = 0;i < count;i++, in++)
3501 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3502 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3503 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3504 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3505 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3506 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3507 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3508 // svector/tvector are calculated later in face loading
3509 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3510 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3511 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3512 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3513 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3514 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3515 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3516 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3517 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3518 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3519 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3520 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3521 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3525 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3531 in = (void *)(mod_base + l->fileofs);
3532 if (l->filelen % sizeof(int[3]))
3533 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3534 count = l->filelen / sizeof(*in);
3535 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3537 loadmodel->brushq3.num_triangles = count / 3;
3538 loadmodel->brushq3.data_element3i = out;
3539 loadmodel->brushq3.data_neighbor3i = out + count;
3541 for (i = 0;i < count;i++, in++, out++)
3543 *out = LittleLong(*in);
3544 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3546 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3552 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3560 in = (void *)(mod_base + l->fileofs);
3561 if (l->filelen % sizeof(*in))
3562 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3563 count = l->filelen / sizeof(*in);
3564 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3566 loadmodel->brushq3.data_lightmaps = out;
3567 loadmodel->brushq3.num_lightmaps = count;
3569 for (i = 0;i < count;i++, in++, out++)
3570 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3573 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3577 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3578 //int *originalelement3i;
3579 //int *originalneighbor3i;
3580 float *originalvertex3f;
3581 //float *originalsvector3f;
3582 //float *originaltvector3f;
3583 //float *originalnormal3f;
3584 float *originalcolor4f;
3585 float *originaltexcoordtexture2f;
3586 float *originaltexcoordlightmap2f;
3589 in = (void *)(mod_base + l->fileofs);
3590 if (l->filelen % sizeof(*in))
3591 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3592 count = l->filelen / sizeof(*in);
3593 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3595 loadmodel->brushq3.data_faces = out;
3596 loadmodel->brushq3.num_faces = count;
3598 for (i = 0;i < count;i++, in++, out++)
3600 // check face type first
3601 out->type = LittleLong(in->type);
3602 if (out->type != Q3FACETYPE_POLYGON
3603 && out->type != Q3FACETYPE_PATCH
3604 && out->type != Q3FACETYPE_MESH
3605 && out->type != Q3FACETYPE_FLARE)
3607 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3608 out->num_vertices = 0;
3609 out->num_triangles = 0;
3610 out->type = 0; // error
3614 n = LittleLong(in->textureindex);
3615 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3617 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3618 out->num_vertices = 0;
3619 out->num_triangles = 0;
3620 out->type = 0; // error
3624 out->texture = loadmodel->brushq3.data_textures + n;
3625 n = LittleLong(in->effectindex);
3626 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3628 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3634 out->effect = loadmodel->brushq3.data_effects + n;
3635 n = LittleLong(in->lightmapindex);
3636 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3638 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3642 out->lightmaptexture = NULL;
3644 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3646 out->firstvertex = LittleLong(in->firstvertex);
3647 out->num_vertices = LittleLong(in->numvertices);
3648 out->firstelement = LittleLong(in->firstelement);
3649 out->num_triangles = LittleLong(in->numelements) / 3;
3650 if (out->num_triangles * 3 != LittleLong(in->numelements))
3652 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));
3653 out->num_vertices = 0;
3654 out->num_triangles = 0;
3655 out->type = 0; // error
3658 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3660 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);
3661 out->num_vertices = 0;
3662 out->num_triangles = 0;
3663 out->type = 0; // error
3666 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3668 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);
3669 out->num_vertices = 0;
3670 out->num_triangles = 0;
3671 out->type = 0; // error
3676 case Q3FACETYPE_POLYGON:
3677 case Q3FACETYPE_MESH:
3678 // no processing necessary
3679 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3680 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3681 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3682 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3683 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3684 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3685 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3686 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3687 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3689 case Q3FACETYPE_PATCH:
3690 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3691 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3692 if (patchsize[0] < 1 || patchsize[1] < 1)
3694 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3695 out->num_vertices = 0;
3696 out->num_triangles = 0;
3697 out->type = 0; // error
3700 // convert patch to Q3FACETYPE_MESH
3701 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3702 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3703 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3704 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3705 finalvertices = finalwidth * finalheight;
3706 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3707 originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3708 //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3709 //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3710 //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3711 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3712 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3713 originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3714 //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
3715 //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3717 originalvertex3f = out->data_vertex3f;
3718 //originalsvector3f = out->data_svector3f;
3719 //originaltvector3f = out->data_tvector3f;
3720 //originalnormal3f = out->data_normal3f;
3721 originalcolor4f = out->data_color4f;
3722 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3723 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3724 //originalelement3i = out->data_element3i;
3725 //originalneighbor3i = out->data_neighbor3i;
3727 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
3728 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3729 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3730 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3731 out->data_color4f = out->data_normal3f + finalvertices * 3;
3732 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3733 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3734 out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
3735 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3736 out->type = Q3FACETYPE_MESH;
3737 out->firstvertex = -1;
3738 out->num_vertices = finalvertices;
3739 out->firstelement = -1;
3740 out->num_triangles = finaltriangles;
3741 // generate geometry
3742 // (note: normals are skipped because they get recalculated)
3743 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3744 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3745 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3746 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3747 // generate elements
3748 e = out->data_element3i;
3749 for (y = 0;y < finalheight - 1;y++)
3751 row0 = (y + 0) * finalwidth;
3752 row1 = (y + 1) * finalwidth;
3753 for (x = 0;x < finalwidth - 1;x++)
3765 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3766 if (developer.integer)
3768 if (out->num_triangles < finaltriangles)
3769 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);
3771 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);
3773 // q3map does not put in collision brushes for curves... ugh
3774 out->collisions = true;
3776 case Q3FACETYPE_FLARE:
3777 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3779 out->num_vertices = 0;
3780 out->num_triangles = 0;
3784 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
3785 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3787 if (invalidelements)
3789 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);
3790 for (j = 0;j < out->num_triangles * 3;j++)
3792 Con_Printf(" %i", out->data_element3i[j]);
3793 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3794 out->data_element3i[j] = 0;
3798 // for shadow volumes
3799 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
3800 // for per pixel lighting
3801 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);
3802 // calculate a bounding box
3803 VectorClear(out->mins);
3804 VectorClear(out->maxs);
3805 if (out->num_vertices)
3807 VectorCopy(out->data_vertex3f, out->mins);
3808 VectorCopy(out->data_vertex3f, out->maxs);
3809 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
3811 out->mins[0] = min(out->mins[0], v[0]);
3812 out->maxs[0] = max(out->maxs[0], v[0]);
3813 out->mins[1] = min(out->mins[1], v[1]);
3814 out->maxs[1] = max(out->maxs[1], v[1]);
3815 out->mins[2] = min(out->mins[2], v[2]);
3816 out->maxs[2] = max(out->maxs[2], v[2]);
3818 out->mins[0] -= 1.0f;
3819 out->mins[1] -= 1.0f;
3820 out->mins[2] -= 1.0f;
3821 out->maxs[0] += 1.0f;
3822 out->maxs[1] += 1.0f;
3823 out->maxs[2] += 1.0f;
3827 // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
3830 int totalverts, totaltris;
3831 int originalnum_vertices;
3832 float *originaldata_vertex3f;
3833 float *originaldata_texcoordtexture2f;
3834 float *originaldata_texcoordlightmap2f;
3835 float *originaldata_svector3f;
3836 float *originaldata_tvector3f;
3837 float *originaldata_normal3f;
3838 float *originaldata_color4f;
3839 int originalnum_triangles;
3840 int *originaldata_element3i;
3841 int *originaldata_neighbor3i;
3845 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
3849 totalverts += out->num_vertices;
3850 totaltris += out->num_triangles;
3853 originalnum_vertices = loadmodel->brushq3.num_vertices;
3854 originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
3855 originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
3856 originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
3857 originaldata_svector3f = loadmodel->brushq3.data_svector3f;
3858 originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
3859 originaldata_normal3f = loadmodel->brushq3.data_normal3f;
3860 originaldata_color4f = loadmodel->brushq3.data_color4f;
3861 originalnum_triangles = loadmodel->brushq3.num_triangles;
3862 originaldata_element3i = loadmodel->brushq3.data_element3i;
3863 originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
3864 loadmodel->brushq3.num_vertices = totalverts;
3865 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
3866 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
3867 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
3868 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
3869 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
3870 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
3871 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
3872 loadmodel->brushq3.num_triangles = totaltris;
3873 loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
3874 loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
3877 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
3881 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
3882 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
3883 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
3884 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
3885 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
3886 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
3887 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
3888 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
3889 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
3890 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
3891 if (out->firstvertex == -1)
3892 Mem_Free(out->data_vertex3f);
3893 if (out->firstelement == -1)
3894 Mem_Free(out->data_element3i);
3895 out->firstvertex = totalverts;
3896 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3897 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3898 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3899 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3900 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3901 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3902 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3903 out->firstelement = totaltris * 3;
3904 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3905 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3906 //for (j = 0;j < out->numtriangles * 3;j++)
3907 // out->data_element3i[j] += totalverts - out->firstvertex;
3908 totalverts += out->num_vertices;
3909 totaltris += out->num_triangles;
3911 Mem_Free(originaldata_vertex3f);
3912 Mem_Free(originaldata_element3i);
3917 static void Mod_Q3BSP_LoadModels(lump_t *l)
3921 int i, j, n, c, count;
3923 in = (void *)(mod_base + l->fileofs);
3924 if (l->filelen % sizeof(*in))
3925 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3926 count = l->filelen / sizeof(*in);
3927 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3929 loadmodel->brushq3.data_models = out;
3930 loadmodel->brushq3.num_models = count;
3932 for (i = 0;i < count;i++, in++, out++)
3934 for (j = 0;j < 3;j++)
3936 out->mins[j] = LittleFloat(in->mins[j]);
3937 out->maxs[j] = LittleFloat(in->maxs[j]);
3939 n = LittleLong(in->firstface);
3940 c = LittleLong(in->numfaces);
3941 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3942 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3943 out->firstface = loadmodel->brushq3.data_faces + n;
3945 n = LittleLong(in->firstbrush);
3946 c = LittleLong(in->numbrushes);
3947 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3948 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3949 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3950 out->numbrushes = c;
3954 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3960 in = (void *)(mod_base + l->fileofs);
3961 if (l->filelen % sizeof(*in))
3962 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3963 count = l->filelen / sizeof(*in);
3964 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3966 loadmodel->brushq3.data_leafbrushes = out;
3967 loadmodel->brushq3.num_leafbrushes = count;
3969 for (i = 0;i < count;i++, in++, out++)
3971 n = LittleLong(*in);
3972 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3973 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3974 *out = loadmodel->brushq3.data_brushes + n;
3978 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3984 in = (void *)(mod_base + l->fileofs);
3985 if (l->filelen % sizeof(*in))
3986 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3987 count = l->filelen / sizeof(*in);
3988 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3990 loadmodel->brushq3.data_leaffaces = out;
3991 loadmodel->brushq3.num_leaffaces = count;
3993 for (i = 0;i < count;i++, in++, out++)
3995 n = LittleLong(*in);
3996 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3997 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3998 *out = loadmodel->brushq3.data_faces + n;
4002 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4006 int i, j, n, c, count;
4008 in = (void *)(mod_base + l->fileofs);
4009 if (l->filelen % sizeof(*in))
4010 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4011 count = l->filelen / sizeof(*in);
4012 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4014 loadmodel->brushq3.data_leafs = out;
4015 loadmodel->brushq3.num_leafs = count;
4017 for (i = 0;i < count;i++, in++, out++)
4019 out->isnode = false;
4021 out->clusterindex = LittleLong(in->clusterindex);
4022 out->areaindex = LittleLong(in->areaindex);
4023 for (j = 0;j < 3;j++)
4025 // yes the mins/maxs are ints
4026 out->mins[j] = LittleLong(in->mins[j]);
4027 out->maxs[j] = LittleLong(in->maxs[j]);
4029 n = LittleLong(in->firstleafface);
4030 c = LittleLong(in->numleaffaces);
4031 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4032 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4033 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4034 out->numleaffaces = c;
4035 n = LittleLong(in->firstleafbrush);
4036 c = LittleLong(in->numleafbrushes);
4037 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4038 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4039 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4040 out->numleafbrushes = c;
4044 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4047 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4048 node->parent = parent;
4051 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4052 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4056 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4062 in = (void *)(mod_base + l->fileofs);
4063 if (l->filelen % sizeof(*in))
4064 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4065 count = l->filelen / sizeof(*in);
4066 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4068 loadmodel->brushq3.data_nodes = out;
4069 loadmodel->brushq3.num_nodes = count;
4071 for (i = 0;i < count;i++, in++, out++)
4075 n = LittleLong(in->planeindex);
4076 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4077 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4078 out->plane = loadmodel->brushq3.data_planes + n;
4079 for (j = 0;j < 2;j++)
4081 n = LittleLong(in->childrenindex[j]);
4084 if (n >= loadmodel->brushq3.num_nodes)
4085 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4086 out->children[j] = loadmodel->brushq3.data_nodes + n;
4091 if (n >= loadmodel->brushq3.num_leafs)
4092 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4093 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4096 for (j = 0;j < 3;j++)
4098 // yes the mins/maxs are ints
4099 out->mins[j] = LittleLong(in->mins[j]);
4100 out->maxs[j] = LittleLong(in->maxs[j]);
4104 // set the parent pointers
4105 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4108 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4111 q3dlightgrid_t *out;
4114 if (l->filelen == 0)
4117 in = (void *)(mod_base + l->fileofs);
4118 if (l->filelen % sizeof(*in))
4119 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4120 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4121 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4122 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4123 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4124 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4125 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4126 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4127 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4128 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4129 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4130 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4131 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4132 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4133 if (l->filelen < count * (int)sizeof(*in))
4134 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]);
4135 if (l->filelen != count * (int)sizeof(*in))
4136 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4138 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4139 loadmodel->brushq3.data_lightgrid = out;
4140 loadmodel->brushq3.num_lightgrid = count;
4142 // no swapping or validation necessary
4143 memcpy(out, in, count * (int)sizeof(*out));
4145 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]);
4146 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]);
4149 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4154 if (l->filelen == 0)
4157 in = (void *)(mod_base + l->fileofs);
4159 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4161 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4162 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4163 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4164 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4165 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4166 if (l->filelen < totalchains + (int)sizeof(*in))
4167 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);
4169 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4170 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4173 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4175 // FIXME: finish this code
4176 VectorCopy(in, out);
4179 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4181 int i, j, k, index[3];
4182 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4183 q3dlightgrid_t *a, *s;
4184 // FIXME: write this
4185 if (!model->brushq3.num_lightgrid)
4187 ambientcolor[0] = 1;
4188 ambientcolor[1] = 1;
4189 ambientcolor[2] = 1;
4192 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4193 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4194 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4195 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4196 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4197 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4198 index[0] = (int)floor(transformed[0]);
4199 index[1] = (int)floor(transformed[1]);
4200 index[2] = (int)floor(transformed[2]);
4201 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4202 // now lerp the values
4203 VectorClear(diffusenormal);
4204 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4205 for (k = 0;k < 2;k++)
4207 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4208 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4210 for (j = 0;j < 2;j++)
4212 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4213 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4215 for (i = 0;i < 2;i++)
4217 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4218 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4220 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4221 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4222 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4223 pitch = s->diffusepitch * M_PI / 128;
4224 yaw = s->diffuseyaw * M_PI / 128;
4225 sinpitch = sin(pitch);
4226 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4227 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4228 diffusenormal[2] += blend * (cos(pitch));
4229 //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)));
4233 VectorNormalize(diffusenormal);
4234 //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]);
4237 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)
4239 int i, startside, endside;
4240 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4244 if (startfrac >= trace->fraction)
4246 // note: all line fragments past first impact fraction are ignored
4247 while (node->isnode)
4249 // recurse down node sides
4250 dist1 = PlaneDiff(start, node->plane);
4251 dist2 = PlaneDiff(end, node->plane);
4252 startside = dist1 < 0;
4253 endside = dist2 < 0;
4254 if (startside == endside)
4256 // most of the time the line fragment is on one side of the plane
4257 node = node->children[startside];
4261 // line crosses node plane, split the line
4262 midfrac = dist1 / (dist1 - dist2);
4263 VectorLerp(linestart, midfrac, lineend, mid);
4264 // take the near side first
4265 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4266 if (midfrac < trace->fraction)
4267 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4272 segmentmins[0] = min(start[0], end[0]);
4273 segmentmins[1] = min(start[1], end[1]);
4274 segmentmins[2] = min(start[2], end[2]);
4275 segmentmaxs[0] = max(start[0], end[0]);
4276 segmentmaxs[1] = max(start[1], end[1]);
4277 segmentmaxs[2] = max(start[2], end[2]);
4278 leaf = (q3mleaf_t *)node;
4279 for (i = 0;i < leaf->numleafbrushes;i++)
4281 if (startfrac >= trace->fraction)
4283 brush = leaf->firstleafbrush[i]->colbrushf;
4284 if (brush && brush->markframe != markframe)
4286 brush->markframe = markframe;
4287 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4288 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4291 if (mod_q3bsp_curves_collisions.integer)
4293 for (i = 0;i < leaf->numleaffaces;i++)
4295 if (startfrac >= trace->fraction)
4297 face = leaf->firstleafface[i];
4298 if (face->collisions && face->collisionmarkframe != markframe)
4300 face->collisionmarkframe = markframe;
4301 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4302 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4308 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)
4311 float nodesegmentmins[3], nodesegmentmaxs[3];
4315 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4316 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4317 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4318 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4319 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4320 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4321 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4325 // recurse down node sides
4326 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4329 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4330 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4332 else if (sides == 2)
4333 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4335 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4337 dist = node->plane->dist - (1.0f / 8.0f);
4338 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4340 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4342 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4348 dist = node->plane->dist + (1.0f / 8.0f);
4349 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4351 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4353 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4359 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4361 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4363 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4369 leaf = (q3mleaf_t *)node;
4370 for (i = 0;i < leaf->numleafbrushes;i++)
4372 brush = leaf->firstleafbrush[i]->colbrushf;
4373 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4375 brush->markframe = markframe;
4376 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4379 if (mod_q3bsp_curves_collisions.integer)
4381 for (i = 0;i < leaf->numleaffaces;i++)
4383 face = leaf->firstleafface[i];
4384 // 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
4385 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4386 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4392 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)
4395 float segmentmins[3], segmentmaxs[3];
4396 colbrushf_t *thisbrush_start, *thisbrush_end;
4397 matrix4x4_t startmatrix, endmatrix;
4398 static int markframe = 0;
4400 memset(trace, 0, sizeof(*trace));
4401 trace->fraction = 1;
4402 trace->hitsupercontentsmask = hitsupercontentsmask;
4403 Matrix4x4_CreateIdentity(&startmatrix);
4404 Matrix4x4_CreateIdentity(&endmatrix);
4405 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4406 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4407 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4408 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4409 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4410 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4411 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4414 if (model->brushq3.submodel)
4416 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4417 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4418 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4419 if (mod_q3bsp_curves_collisions.integer)
4421 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4423 face = model->brushq3.data_thismodel->firstface + i;
4424 if (face->collisions)
4425 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4430 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4434 // box trace, performed as brush trace
4435 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4436 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4437 if (model->brushq3.submodel)
4439 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4440 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4441 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4442 if (mod_q3bsp_curves_collisions.integer)
4444 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4446 face = model->brushq3.data_thismodel->firstface + i;
4447 if (face->collisions)
4448 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4453 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4458 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)
4465 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4466 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4469 // node - recurse down the BSP tree
4470 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4473 node = node->children[0];
4476 node = node->children[1];
4478 default: // crossing
4479 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4481 node = node->children[1];
4488 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4490 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4493 //Returns PVS data for a given point
4494 //(note: can return NULL)
4495 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4498 Mod_CheckLoaded(model);
4499 node = model->brushq3.data_nodes;
4500 while (node->isnode)
4501 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4502 if (((q3mleaf_t *)node)->clusterindex >= 0)
4503 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4508 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4514 while (node->isnode)
4516 d = PlaneDiff(org, node->plane);
4518 node = node->children[0];
4519 else if (d < -radius)
4520 node = node->children[1];
4523 // go down both sides
4524 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4525 node = node->children[1];
4528 // if this is a leaf with a pvs, accumulate the pvs bits
4529 if (((q3mleaf_t *)node)->clusterindex >= 0)
4531 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4532 for (i = 0;i < pvsbytes;i++)
4533 pvsbuffer[i] |= pvs[i];
4538 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4539 //of the given point.
4540 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4542 int bytes = model->brushq3.num_pvschainlength;
4543 bytes = min(bytes, pvsbufferlength);
4544 memset(pvsbuffer, 0, bytes);
4545 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4550 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4552 int supercontents = 0;
4553 if (nativecontents & Q2CONTENTS_SOLID)
4554 supercontents |= SUPERCONTENTS_SOLID;
4555 if (nativecontents & Q2CONTENTS_WATER)
4556 supercontents |= SUPERCONTENTS_WATER;
4557 if (nativecontents & Q2CONTENTS_SLIME)
4558 supercontents |= SUPERCONTENTS_SLIME;
4559 if (nativecontents & Q2CONTENTS_LAVA)
4560 supercontents |= SUPERCONTENTS_LAVA;
4561 return supercontents;
4564 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4566 int nativecontents = 0;
4567 if (supercontents & SUPERCONTENTS_SOLID)
4568 nativecontents |= Q2CONTENTS_SOLID;
4569 if (supercontents & SUPERCONTENTS_WATER)
4570 nativecontents |= Q2CONTENTS_WATER;
4571 if (supercontents & SUPERCONTENTS_SLIME)
4572 nativecontents |= Q2CONTENTS_SLIME;
4573 if (supercontents & SUPERCONTENTS_LAVA)
4574 nativecontents |= Q2CONTENTS_LAVA;
4575 return nativecontents;
4578 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4579 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4580 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4581 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);
4582 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4585 q3dheader_t *header;
4586 float corner[3], yawradius, modelradius;
4588 mod->type = mod_brushq3;
4592 header = (q3dheader_t *)buffer;
4594 i = LittleLong(header->version);
4595 if (i != Q3BSPVERSION)
4596 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4597 if (loadmodel->isworldmodel)
4599 Cvar_SetValue("halflifebsp", false);
4600 // until we get a texture for it...
4604 mod->soundfromcenter = true;
4605 mod->TraceBox = Mod_Q3BSP_TraceBox;
4606 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4607 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4608 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4609 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4610 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4611 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4612 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4613 //mod->DrawSky = R_Q3BSP_DrawSky;
4614 mod->Draw = R_Q3BSP_Draw;
4615 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4616 mod->DrawLight = R_Q3BSP_DrawLight;
4618 mod_base = (qbyte *)header;
4620 // swap all the lumps
4621 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4622 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4624 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4625 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4626 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4627 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4628 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4629 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4630 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4631 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4632 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4633 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4634 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4635 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4636 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4637 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4638 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4639 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4640 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4641 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4643 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4650 // LordHavoc: only register submodels if it is the world
4651 // (prevents bsp models from replacing world submodels)
4652 if (!loadmodel->isworldmodel)
4654 // duplicate the basic information
4655 sprintf(name, "*%i", i);
4656 mod = Mod_FindName(name);
4658 strcpy(mod->name, name);
4659 // textures and memory belong to the main model
4660 mod->texturepool = NULL;
4661 mod->mempool = NULL;
4663 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4664 mod->brushq3.submodel = i;
4666 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4667 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4668 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4669 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4670 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4671 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4672 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4673 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4674 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4675 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4676 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4677 mod->yawmins[2] = mod->normalmins[2];
4678 mod->yawmaxs[2] = mod->normalmaxs[2];
4679 mod->radius = modelradius;
4680 mod->radius2 = modelradius * modelradius;
4682 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
4683 if (mod->brushq3.data_thismodel->firstface[j].texture->renderflags & Q3MTEXTURERENDERFLAGS_SKY)
4685 if (j < mod->brushq3.data_thismodel->numfaces)
4686 mod->DrawSky = R_Q3BSP_DrawSky;
4690 void Mod_IBSP_Load(model_t *mod, void *buffer)
4692 int i = LittleLong(((int *)buffer)[1]);
4693 if (i == Q3BSPVERSION)
4694 Mod_Q3BSP_Load(mod,buffer);
4695 else if (i == Q2BSPVERSION)
4696 Mod_Q2BSP_Load(mod,buffer);
4698 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4701 void Mod_MAP_Load(model_t *mod, void *buffer)
4703 Host_Error("Mod_MAP_Load: not yet implemented\n");