2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
38 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
39 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
41 void Mod_BrushInit(void)
43 // Cvar_RegisterVariable(&r_subdivide_size);
44 Cvar_RegisterVariable(&halflifebsp);
45 Cvar_RegisterVariable(&r_novis);
46 Cvar_RegisterVariable(&r_miplightmaps);
47 Cvar_RegisterVariable(&r_lightmaprgba);
48 Cvar_RegisterVariable(&r_nosurftextures);
49 Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
50 Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
51 Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
52 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
62 Mod_CheckLoaded(model);
64 // LordHavoc: modified to start at first clip node,
65 // in other words: first node of the (sub)model
66 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
67 while (node->contents == 0)
68 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
70 return (mleaf_t *)node;
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
77 leaf = Mod_Q1BSP_PointInLeaf(model, p);
80 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
83 memcpy(out, leaf->ambient_sound_level, i);
89 memset(out, 0, outsize);
93 static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
97 if (node->contents < 0)
100 if (node->contents == CONTENTS_SOLID)
102 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
106 // node - recurse down the BSP tree
107 switch (BoxOnPlaneSide(mins, maxs, node->plane))
110 node = node->children[0];
113 node = node->children[1];
116 if (node->children[0]->contents != CONTENTS_SOLID)
117 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
119 node = node->children[1];
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
128 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
137 return CONTENTS_EMPTY;
139 Mod_CheckLoaded(model);
141 // LordHavoc: modified to start at first clip node,
142 // in other words: first node of the (sub)model
143 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
144 while (node->contents == 0)
145 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
147 return ((mleaf_t *)node)->contents;
151 typedef struct findnonsolidlocationinfo_s
159 findnonsolidlocationinfo_t;
162 extern cvar_t samelevel;
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
166 int i, surfnum, k, *tri, *mark;
167 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
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 for (i = 0;i < count;i++, in++, out++)
3324 strlcpy (out->name, in->name, sizeof (out->name));
3325 out->surfaceflags = LittleLong(in->surfaceflags);
3326 out->nativecontents = LittleLong(in->contents);
3327 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3328 out->renderflags = 0;
3329 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3330 out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3331 if (!strncmp(out->name, "textures/skies/", 15))
3332 out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY;
3335 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3339 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3345 in = (void *)(mod_base + l->fileofs);
3346 if (l->filelen % sizeof(*in))
3347 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3348 count = l->filelen / sizeof(*in);
3349 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3351 loadmodel->brushq3.data_planes = out;
3352 loadmodel->brushq3.num_planes = count;
3354 for (i = 0;i < count;i++, in++, out++)
3356 out->normal[0] = LittleLong(in->normal[0]);
3357 out->normal[1] = LittleLong(in->normal[1]);
3358 out->normal[2] = LittleLong(in->normal[2]);
3359 out->dist = LittleLong(in->dist);
3364 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3367 q3mbrushside_t *out;
3370 in = (void *)(mod_base + l->fileofs);
3371 if (l->filelen % sizeof(*in))
3372 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3373 count = l->filelen / sizeof(*in);
3374 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3376 loadmodel->brushq3.data_brushsides = out;
3377 loadmodel->brushq3.num_brushsides = count;
3379 for (i = 0;i < count;i++, in++, out++)
3381 n = LittleLong(in->planeindex);
3382 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3383 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3384 out->plane = loadmodel->brushq3.data_planes + n;
3385 n = LittleLong(in->textureindex);
3386 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3387 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3388 out->texture = loadmodel->brushq3.data_textures + n;
3392 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3396 int i, j, n, c, count, maxplanes;
3398 winding_t *temp1, *temp2;
3400 in = (void *)(mod_base + l->fileofs);
3401 if (l->filelen % sizeof(*in))
3402 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3403 count = l->filelen / sizeof(*in);
3404 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3406 loadmodel->brushq3.data_brushes = out;
3407 loadmodel->brushq3.num_brushes = count;
3409 temp1 = Winding_New(64);
3410 temp2 = Winding_New(64);
3415 for (i = 0;i < count;i++, in++, out++)
3417 n = LittleLong(in->firstbrushside);
3418 c = LittleLong(in->numbrushsides);
3419 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3420 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3421 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3422 out->numbrushsides = c;
3423 n = LittleLong(in->textureindex);
3424 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3425 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3426 out->texture = loadmodel->brushq3.data_textures + n;
3428 // make a list of mplane_t structs to construct a colbrush from
3429 if (maxplanes < out->numbrushsides)
3431 maxplanes = out->numbrushsides;
3434 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3436 for (j = 0;j < out->numbrushsides;j++)
3438 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3439 planes[j].dist = out->firstbrushside[j].plane->dist;
3441 // make the colbrush from the planes
3442 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
3446 Winding_Free(temp1);
3447 Winding_Free(temp2);
3450 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3456 in = (void *)(mod_base + l->fileofs);
3457 if (l->filelen % sizeof(*in))
3458 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3459 count = l->filelen / sizeof(*in);
3460 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3462 loadmodel->brushq3.data_effects = out;
3463 loadmodel->brushq3.num_effects = count;
3465 for (i = 0;i < count;i++, in++, out++)
3467 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3468 n = LittleLong(in->brushindex);
3469 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3470 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3471 out->brush = loadmodel->brushq3.data_brushes + n;
3472 out->unknown = LittleLong(in->unknown);
3476 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3481 in = (void *)(mod_base + l->fileofs);
3482 if (l->filelen % sizeof(*in))
3483 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3484 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3485 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3486 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3487 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3488 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3489 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3490 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3491 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3493 for (i = 0;i < count;i++, in++)
3495 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3496 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3497 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3498 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3499 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3500 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3501 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3502 // svector/tvector are calculated later in face loading
3503 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3504 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3505 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3506 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3507 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3508 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3509 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3510 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3511 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3512 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3513 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3514 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3515 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3519 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3525 in = (void *)(mod_base + l->fileofs);
3526 if (l->filelen % sizeof(int[3]))
3527 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3528 count = l->filelen / sizeof(*in);
3529 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3531 loadmodel->brushq3.num_triangles = count / 3;
3532 loadmodel->brushq3.data_element3i = out;
3533 loadmodel->brushq3.data_neighbor3i = out + count;
3535 for (i = 0;i < count;i++, in++, out++)
3537 *out = LittleLong(*in);
3538 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3540 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3546 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3554 in = (void *)(mod_base + l->fileofs);
3555 if (l->filelen % sizeof(*in))
3556 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3557 count = l->filelen / sizeof(*in);
3558 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3560 loadmodel->brushq3.data_lightmaps = out;
3561 loadmodel->brushq3.num_lightmaps = count;
3563 for (i = 0;i < count;i++, in++, out++)
3564 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3567 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3571 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3572 //int *originalelement3i;
3573 //int *originalneighbor3i;
3574 float *originalvertex3f;
3575 //float *originalsvector3f;
3576 //float *originaltvector3f;
3577 //float *originalnormal3f;
3578 float *originalcolor4f;
3579 float *originaltexcoordtexture2f;
3580 float *originaltexcoordlightmap2f;
3583 in = (void *)(mod_base + l->fileofs);
3584 if (l->filelen % sizeof(*in))
3585 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3586 count = l->filelen / sizeof(*in);
3587 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3589 loadmodel->brushq3.data_faces = out;
3590 loadmodel->brushq3.num_faces = count;
3592 for (i = 0;i < count;i++, in++, out++)
3594 // check face type first
3595 out->type = LittleLong(in->type);
3596 if (out->type != Q3FACETYPE_POLYGON
3597 && out->type != Q3FACETYPE_PATCH
3598 && out->type != Q3FACETYPE_MESH
3599 && out->type != Q3FACETYPE_FLARE)
3601 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3602 out->num_vertices = 0;
3603 out->num_triangles = 0;
3604 out->type = 0; // error
3608 n = LittleLong(in->textureindex);
3609 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3611 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3612 out->num_vertices = 0;
3613 out->num_triangles = 0;
3614 out->type = 0; // error
3618 out->texture = loadmodel->brushq3.data_textures + n;
3619 n = LittleLong(in->effectindex);
3620 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3622 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3628 out->effect = loadmodel->brushq3.data_effects + n;
3629 n = LittleLong(in->lightmapindex);
3630 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3632 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3636 out->lightmaptexture = NULL;
3638 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3640 out->firstvertex = LittleLong(in->firstvertex);
3641 out->num_vertices = LittleLong(in->numvertices);
3642 out->firstelement = LittleLong(in->firstelement);
3643 out->num_triangles = LittleLong(in->numelements) / 3;
3644 if (out->num_triangles * 3 != LittleLong(in->numelements))
3646 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));
3647 out->num_vertices = 0;
3648 out->num_triangles = 0;
3649 out->type = 0; // error
3652 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3654 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);
3655 out->num_vertices = 0;
3656 out->num_triangles = 0;
3657 out->type = 0; // error
3660 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3662 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);
3663 out->num_vertices = 0;
3664 out->num_triangles = 0;
3665 out->type = 0; // error
3670 case Q3FACETYPE_POLYGON:
3671 case Q3FACETYPE_MESH:
3672 // no processing necessary
3673 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3674 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3675 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3676 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3677 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3678 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3679 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3680 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3681 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3683 case Q3FACETYPE_PATCH:
3684 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3685 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3686 if (patchsize[0] < 1 || patchsize[1] < 1)
3688 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3689 out->num_vertices = 0;
3690 out->num_triangles = 0;
3691 out->type = 0; // error
3694 // convert patch to Q3FACETYPE_MESH
3695 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3696 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3697 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3698 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3699 finalvertices = finalwidth * finalheight;
3700 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3701 originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3702 //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3703 //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3704 //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3705 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3706 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3707 originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3708 //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
3709 //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3711 originalvertex3f = out->data_vertex3f;
3712 //originalsvector3f = out->data_svector3f;
3713 //originaltvector3f = out->data_tvector3f;
3714 //originalnormal3f = out->data_normal3f;
3715 originalcolor4f = out->data_color4f;
3716 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3717 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3718 //originalelement3i = out->data_element3i;
3719 //originalneighbor3i = out->data_neighbor3i;
3721 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
3722 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3723 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3724 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3725 out->data_color4f = out->data_normal3f + finalvertices * 3;
3726 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3727 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3728 out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
3729 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3730 out->type = Q3FACETYPE_MESH;
3731 out->firstvertex = -1;
3732 out->num_vertices = finalvertices;
3733 out->firstelement = -1;
3734 out->num_triangles = finaltriangles;
3735 // generate geometry
3736 // (note: normals are skipped because they get recalculated)
3737 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3738 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3739 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3740 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3741 // generate elements
3742 e = out->data_element3i;
3743 for (y = 0;y < finalheight - 1;y++)
3745 row0 = (y + 0) * finalwidth;
3746 row1 = (y + 1) * finalwidth;
3747 for (x = 0;x < finalwidth - 1;x++)
3759 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3760 if (developer.integer)
3762 if (out->num_triangles < finaltriangles)
3763 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);
3765 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);
3767 // q3map does not put in collision brushes for curves... ugh
3768 out->collisions = true;
3770 case Q3FACETYPE_FLARE:
3771 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3773 out->num_vertices = 0;
3774 out->num_triangles = 0;
3778 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
3779 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3781 if (invalidelements)
3783 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);
3784 for (j = 0;j < out->num_triangles * 3;j++)
3786 Con_Printf(" %i", out->data_element3i[j]);
3787 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3788 out->data_element3i[j] = 0;
3792 // for shadow volumes
3793 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
3794 // for per pixel lighting
3795 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);
3796 // calculate a bounding box
3797 VectorClear(out->mins);
3798 VectorClear(out->maxs);
3799 if (out->num_vertices)
3801 VectorCopy(out->data_vertex3f, out->mins);
3802 VectorCopy(out->data_vertex3f, out->maxs);
3803 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
3805 out->mins[0] = min(out->mins[0], v[0]);
3806 out->maxs[0] = max(out->maxs[0], v[0]);
3807 out->mins[1] = min(out->mins[1], v[1]);
3808 out->maxs[1] = max(out->maxs[1], v[1]);
3809 out->mins[2] = min(out->mins[2], v[2]);
3810 out->maxs[2] = max(out->maxs[2], v[2]);
3812 out->mins[0] -= 1.0f;
3813 out->mins[1] -= 1.0f;
3814 out->mins[2] -= 1.0f;
3815 out->maxs[0] += 1.0f;
3816 out->maxs[1] += 1.0f;
3817 out->maxs[2] += 1.0f;
3821 // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
3824 int totalverts, totaltris;
3825 int originalnum_vertices;
3826 float *originaldata_vertex3f;
3827 float *originaldata_texcoordtexture2f;
3828 float *originaldata_texcoordlightmap2f;
3829 float *originaldata_svector3f;
3830 float *originaldata_tvector3f;
3831 float *originaldata_normal3f;
3832 float *originaldata_color4f;
3833 int originalnum_triangles;
3834 int *originaldata_element3i;
3835 int *originaldata_neighbor3i;
3839 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
3843 totalverts += out->num_vertices;
3844 totaltris += out->num_triangles;
3847 originalnum_vertices = loadmodel->brushq3.num_vertices;
3848 originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
3849 originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
3850 originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
3851 originaldata_svector3f = loadmodel->brushq3.data_svector3f;
3852 originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
3853 originaldata_normal3f = loadmodel->brushq3.data_normal3f;
3854 originaldata_color4f = loadmodel->brushq3.data_color4f;
3855 originalnum_triangles = loadmodel->brushq3.num_triangles;
3856 originaldata_element3i = loadmodel->brushq3.data_element3i;
3857 originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
3858 loadmodel->brushq3.num_vertices = totalverts;
3859 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
3860 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
3861 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
3862 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
3863 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
3864 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
3865 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
3866 loadmodel->brushq3.num_triangles = totaltris;
3867 loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
3868 loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
3871 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
3875 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
3876 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
3877 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
3878 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
3879 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
3880 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
3881 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
3882 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
3883 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
3884 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
3885 if (out->firstvertex == -1)
3886 Mem_Free(out->data_vertex3f);
3887 if (out->firstelement == -1)
3888 Mem_Free(out->data_element3i);
3889 out->firstvertex = totalverts;
3890 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3891 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3892 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3893 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3894 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3895 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3896 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3897 out->firstelement = totaltris * 3;
3898 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3899 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3900 //for (j = 0;j < out->numtriangles * 3;j++)
3901 // out->data_element3i[j] += totalverts - out->firstvertex;
3902 totalverts += out->num_vertices;
3903 totaltris += out->num_triangles;
3905 Mem_Free(originaldata_vertex3f);
3906 Mem_Free(originaldata_element3i);
3911 static void Mod_Q3BSP_LoadModels(lump_t *l)
3915 int i, j, n, c, count;
3917 in = (void *)(mod_base + l->fileofs);
3918 if (l->filelen % sizeof(*in))
3919 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3920 count = l->filelen / sizeof(*in);
3921 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3923 loadmodel->brushq3.data_models = out;
3924 loadmodel->brushq3.num_models = count;
3926 for (i = 0;i < count;i++, in++, out++)
3928 for (j = 0;j < 3;j++)
3930 out->mins[j] = LittleFloat(in->mins[j]);
3931 out->maxs[j] = LittleFloat(in->maxs[j]);
3933 n = LittleLong(in->firstface);
3934 c = LittleLong(in->numfaces);
3935 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3936 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3937 out->firstface = loadmodel->brushq3.data_faces + n;
3939 n = LittleLong(in->firstbrush);
3940 c = LittleLong(in->numbrushes);
3941 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3942 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3943 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3944 out->numbrushes = c;
3948 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3954 in = (void *)(mod_base + l->fileofs);
3955 if (l->filelen % sizeof(*in))
3956 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3957 count = l->filelen / sizeof(*in);
3958 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3960 loadmodel->brushq3.data_leafbrushes = out;
3961 loadmodel->brushq3.num_leafbrushes = count;
3963 for (i = 0;i < count;i++, in++, out++)
3965 n = LittleLong(*in);
3966 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3967 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3968 *out = loadmodel->brushq3.data_brushes + n;
3972 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3978 in = (void *)(mod_base + l->fileofs);
3979 if (l->filelen % sizeof(*in))
3980 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3981 count = l->filelen / sizeof(*in);
3982 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3984 loadmodel->brushq3.data_leaffaces = out;
3985 loadmodel->brushq3.num_leaffaces = count;
3987 for (i = 0;i < count;i++, in++, out++)
3989 n = LittleLong(*in);
3990 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3991 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3992 *out = loadmodel->brushq3.data_faces + n;
3996 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4000 int i, j, n, c, count;
4002 in = (void *)(mod_base + l->fileofs);
4003 if (l->filelen % sizeof(*in))
4004 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4005 count = l->filelen / sizeof(*in);
4006 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4008 loadmodel->brushq3.data_leafs = out;
4009 loadmodel->brushq3.num_leafs = count;
4011 for (i = 0;i < count;i++, in++, out++)
4013 out->isnode = false;
4015 out->clusterindex = LittleLong(in->clusterindex);
4016 out->areaindex = LittleLong(in->areaindex);
4017 for (j = 0;j < 3;j++)
4019 // yes the mins/maxs are ints
4020 out->mins[j] = LittleLong(in->mins[j]);
4021 out->maxs[j] = LittleLong(in->maxs[j]);
4023 n = LittleLong(in->firstleafface);
4024 c = LittleLong(in->numleaffaces);
4025 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4026 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4027 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4028 out->numleaffaces = c;
4029 n = LittleLong(in->firstleafbrush);
4030 c = LittleLong(in->numleafbrushes);
4031 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4032 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4033 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4034 out->numleafbrushes = c;
4038 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4041 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4042 node->parent = parent;
4045 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4046 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4050 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4056 in = (void *)(mod_base + l->fileofs);
4057 if (l->filelen % sizeof(*in))
4058 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4059 count = l->filelen / sizeof(*in);
4060 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4062 loadmodel->brushq3.data_nodes = out;
4063 loadmodel->brushq3.num_nodes = count;
4065 for (i = 0;i < count;i++, in++, out++)
4069 n = LittleLong(in->planeindex);
4070 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4071 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4072 out->plane = loadmodel->brushq3.data_planes + n;
4073 for (j = 0;j < 2;j++)
4075 n = LittleLong(in->childrenindex[j]);
4078 if (n >= loadmodel->brushq3.num_nodes)
4079 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4080 out->children[j] = loadmodel->brushq3.data_nodes + n;
4085 if (n >= loadmodel->brushq3.num_leafs)
4086 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4087 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4090 for (j = 0;j < 3;j++)
4092 // yes the mins/maxs are ints
4093 out->mins[j] = LittleLong(in->mins[j]);
4094 out->maxs[j] = LittleLong(in->maxs[j]);
4098 // set the parent pointers
4099 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4102 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4105 q3dlightgrid_t *out;
4108 if (l->filelen == 0)
4111 in = (void *)(mod_base + l->fileofs);
4112 if (l->filelen % sizeof(*in))
4113 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4114 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4115 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4116 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4117 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4118 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4119 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4120 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4121 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4122 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4123 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4124 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4125 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4126 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4127 if (l->filelen < count * (int)sizeof(*in))
4128 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]);
4129 if (l->filelen != count * (int)sizeof(*in))
4130 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4132 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4133 loadmodel->brushq3.data_lightgrid = out;
4134 loadmodel->brushq3.num_lightgrid = count;
4136 // no swapping or validation necessary
4137 memcpy(out, in, count * (int)sizeof(*out));
4139 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]);
4140 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]);
4143 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4148 if (l->filelen == 0)
4151 in = (void *)(mod_base + l->fileofs);
4153 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4155 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4156 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4157 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4158 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4159 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4160 if (l->filelen < totalchains + (int)sizeof(*in))
4161 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);
4163 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4164 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4167 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4169 // FIXME: finish this code
4170 VectorCopy(in, out);
4173 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4175 int i, j, k, index[3];
4176 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4177 q3dlightgrid_t *a, *s;
4178 // FIXME: write this
4179 if (!model->brushq3.num_lightgrid)
4181 ambientcolor[0] += 128;
4182 ambientcolor[1] += 128;
4183 ambientcolor[2] += 128;
4186 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4187 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4188 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4189 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4190 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4191 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4192 index[0] = (int)floor(transformed[0]);
4193 index[1] = (int)floor(transformed[1]);
4194 index[2] = (int)floor(transformed[2]);
4195 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4196 // now lerp the values
4197 VectorClear(diffusenormal);
4198 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4199 for (k = 0;k < 2;k++)
4201 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4202 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4204 for (j = 0;j < 2;j++)
4206 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4207 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4209 for (i = 0;i < 2;i++)
4211 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4212 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4214 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4215 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4216 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4217 pitch = s->diffusepitch * M_PI / 128;
4218 yaw = s->diffuseyaw * M_PI / 128;
4219 sinpitch = sin(pitch);
4220 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4221 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4222 diffusenormal[2] += blend * (cos(pitch));
4223 //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)));
4227 VectorNormalize(diffusenormal);
4228 //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]);
4231 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)
4233 int i, startside, endside;
4234 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4238 if (startfrac >= trace->fraction)
4240 // note: all line fragments past first impact fraction are ignored
4241 while (node->isnode)
4243 // recurse down node sides
4244 dist1 = PlaneDiff(start, node->plane);
4245 dist2 = PlaneDiff(end, node->plane);
4246 startside = dist1 < 0;
4247 endside = dist2 < 0;
4248 if (startside == endside)
4250 // most of the time the line fragment is on one side of the plane
4251 node = node->children[startside];
4255 // line crosses node plane, split the line
4256 midfrac = dist1 / (dist1 - dist2);
4257 VectorLerp(linestart, midfrac, lineend, mid);
4258 // take the near side first
4259 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4260 if (midfrac < trace->fraction)
4261 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4266 segmentmins[0] = min(start[0], end[0]);
4267 segmentmins[1] = min(start[1], end[1]);
4268 segmentmins[2] = min(start[2], end[2]);
4269 segmentmaxs[0] = max(start[0], end[0]);
4270 segmentmaxs[1] = max(start[1], end[1]);
4271 segmentmaxs[2] = max(start[2], end[2]);
4272 leaf = (q3mleaf_t *)node;
4273 for (i = 0;i < leaf->numleafbrushes;i++)
4275 if (startfrac >= trace->fraction)
4277 brush = leaf->firstleafbrush[i]->colbrushf;
4278 if (brush && brush->markframe != markframe)
4280 brush->markframe = markframe;
4281 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4282 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4285 if (mod_q3bsp_curves_collisions.integer)
4287 for (i = 0;i < leaf->numleaffaces;i++)
4289 if (startfrac >= trace->fraction)
4291 face = leaf->firstleafface[i];
4292 if (face->collisions && face->collisionmarkframe != markframe)
4294 face->collisionmarkframe = markframe;
4295 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4296 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4302 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)
4305 float nodesegmentmins[3], nodesegmentmaxs[3];
4309 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4310 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4311 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4312 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4313 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4314 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4315 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4319 // recurse down node sides
4320 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4323 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4324 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4326 else if (sides == 2)
4327 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4329 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4331 dist = node->plane->dist - (1.0f / 8.0f);
4332 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4334 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4336 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4342 dist = node->plane->dist + (1.0f / 8.0f);
4343 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4345 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4347 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4353 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4355 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4357 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4363 leaf = (q3mleaf_t *)node;
4364 for (i = 0;i < leaf->numleafbrushes;i++)
4366 brush = leaf->firstleafbrush[i]->colbrushf;
4367 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4369 brush->markframe = markframe;
4370 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4373 if (mod_q3bsp_curves_collisions.integer)
4375 for (i = 0;i < leaf->numleaffaces;i++)
4377 face = leaf->firstleafface[i];
4378 // 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
4379 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4380 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4386 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)
4389 float segmentmins[3], segmentmaxs[3];
4390 colbrushf_t *thisbrush_start, *thisbrush_end;
4391 matrix4x4_t startmatrix, endmatrix;
4392 static int markframe = 0;
4394 memset(trace, 0, sizeof(*trace));
4395 trace->fraction = 1;
4396 trace->hitsupercontentsmask = hitsupercontentsmask;
4397 Matrix4x4_CreateIdentity(&startmatrix);
4398 Matrix4x4_CreateIdentity(&endmatrix);
4399 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4400 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4401 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4402 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4403 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4404 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4405 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4408 if (model->brushq3.submodel)
4410 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4411 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4412 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4413 if (mod_q3bsp_curves_collisions.integer)
4415 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4417 face = model->brushq3.data_thismodel->firstface + i;
4418 if (face->collisions)
4419 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4424 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4428 // box trace, performed as brush trace
4429 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4430 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4431 if (model->brushq3.submodel)
4433 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4434 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4435 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4436 if (mod_q3bsp_curves_collisions.integer)
4438 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4440 face = model->brushq3.data_thismodel->firstface + i;
4441 if (face->collisions)
4442 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4447 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4452 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)
4459 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4460 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4463 // node - recurse down the BSP tree
4464 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4467 node = node->children[0];
4470 node = node->children[1];
4472 default: // crossing
4473 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4475 node = node->children[1];
4482 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4484 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4487 //Returns PVS data for a given point
4488 //(note: can return NULL)
4489 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4492 Mod_CheckLoaded(model);
4493 node = model->brushq3.data_nodes;
4494 while (node->isnode)
4495 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4496 if (((q3mleaf_t *)node)->clusterindex >= 0)
4497 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4502 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4508 while (node->isnode)
4510 d = PlaneDiff(org, node->plane);
4512 node = node->children[0];
4513 else if (d < -radius)
4514 node = node->children[1];
4517 // go down both sides
4518 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4519 node = node->children[1];
4522 // if this is a leaf with a pvs, accumulate the pvs bits
4523 if (((q3mleaf_t *)node)->clusterindex >= 0)
4525 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4526 for (i = 0;i < pvsbytes;i++)
4527 pvsbuffer[i] |= pvs[i];
4532 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4533 //of the given point.
4534 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4536 int bytes = model->brushq3.num_pvschainlength;
4537 bytes = min(bytes, pvsbufferlength);
4538 memset(pvsbuffer, 0, bytes);
4539 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4544 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4546 int supercontents = 0;
4547 if (nativecontents & Q2CONTENTS_SOLID)
4548 supercontents |= SUPERCONTENTS_SOLID;
4549 if (nativecontents & Q2CONTENTS_WATER)
4550 supercontents |= SUPERCONTENTS_WATER;
4551 if (nativecontents & Q2CONTENTS_SLIME)
4552 supercontents |= SUPERCONTENTS_SLIME;
4553 if (nativecontents & Q2CONTENTS_LAVA)
4554 supercontents |= SUPERCONTENTS_LAVA;
4555 return supercontents;
4558 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4560 int nativecontents = 0;
4561 if (supercontents & SUPERCONTENTS_SOLID)
4562 nativecontents |= Q2CONTENTS_SOLID;
4563 if (supercontents & SUPERCONTENTS_WATER)
4564 nativecontents |= Q2CONTENTS_WATER;
4565 if (supercontents & SUPERCONTENTS_SLIME)
4566 nativecontents |= Q2CONTENTS_SLIME;
4567 if (supercontents & SUPERCONTENTS_LAVA)
4568 nativecontents |= Q2CONTENTS_LAVA;
4569 return nativecontents;
4572 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4573 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4574 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4575 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);
4576 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4579 q3dheader_t *header;
4580 float corner[3], yawradius, modelradius;
4582 mod->type = mod_brushq3;
4586 header = (q3dheader_t *)buffer;
4588 i = LittleLong(header->version);
4589 if (i != Q3BSPVERSION)
4590 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4591 if (loadmodel->isworldmodel)
4593 Cvar_SetValue("halflifebsp", false);
4594 // until we get a texture for it...
4598 mod->soundfromcenter = true;
4599 mod->TraceBox = Mod_Q3BSP_TraceBox;
4600 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4601 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4602 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4603 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4604 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4605 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4606 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4607 //mod->DrawSky = R_Q3BSP_DrawSky;
4608 mod->Draw = R_Q3BSP_Draw;
4609 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4610 mod->DrawLight = R_Q3BSP_DrawLight;
4612 mod_base = (qbyte *)header;
4614 // swap all the lumps
4615 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4616 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4618 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4619 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4620 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4621 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4622 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4623 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4624 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4625 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4626 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4627 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4628 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4629 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4630 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4631 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4632 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4633 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4634 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4635 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4637 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4644 // LordHavoc: only register submodels if it is the world
4645 // (prevents bsp models from replacing world submodels)
4646 if (!loadmodel->isworldmodel)
4648 // duplicate the basic information
4649 sprintf(name, "*%i", i);
4650 mod = Mod_FindName(name);
4652 strcpy(mod->name, name);
4653 // textures and memory belong to the main model
4654 mod->texturepool = NULL;
4655 mod->mempool = NULL;
4657 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4658 mod->brushq3.submodel = i;
4660 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4661 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4662 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4663 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4664 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4665 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4666 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4667 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4668 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4669 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4670 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4671 mod->yawmins[2] = mod->normalmins[2];
4672 mod->yawmaxs[2] = mod->normalmaxs[2];
4673 mod->radius = modelradius;
4674 mod->radius2 = modelradius * modelradius;
4676 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
4677 if (mod->brushq3.data_thismodel->firstface[j].texture->renderflags & Q3MTEXTURERENDERFLAGS_SKY)
4679 if (j < mod->brushq3.data_thismodel->numfaces)
4680 mod->DrawSky = R_Q3BSP_DrawSky;
4684 void Mod_IBSP_Load(model_t *mod, void *buffer)
4686 int i = LittleLong(((int *)buffer)[1]);
4687 if (i == Q3BSPVERSION)
4688 Mod_Q3BSP_Load(mod,buffer);
4689 else if (i == Q2BSPVERSION)
4690 Mod_Q2BSP_Load(mod,buffer);
4692 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4695 void Mod_MAP_Load(model_t *mod, void *buffer)
4697 Host_Error("Mod_MAP_Load: not yet implemented\n");