2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
38 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
39 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "0"};
41 void Mod_BrushInit(void)
43 // Cvar_RegisterVariable(&r_subdivide_size);
44 Cvar_RegisterVariable(&halflifebsp);
45 Cvar_RegisterVariable(&r_novis);
46 Cvar_RegisterVariable(&r_miplightmaps);
47 Cvar_RegisterVariable(&r_lightmaprgba);
48 Cvar_RegisterVariable(&r_nosurftextures);
49 Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
50 Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
51 Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
52 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
62 Mod_CheckLoaded(model);
64 // LordHavoc: modified to start at first clip node,
65 // in other words: first node of the (sub)model
66 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
67 while (node->contents == 0)
68 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
70 return (mleaf_t *)node;
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
77 leaf = Mod_Q1BSP_PointInLeaf(model, p);
80 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
83 memcpy(out, leaf->ambient_sound_level, i);
89 memset(out, 0, outsize);
93 static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
97 if (node->contents < 0)
100 if (node->contents == CONTENTS_SOLID)
102 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
106 // node - recurse down the BSP tree
107 switch (BoxOnPlaneSide(mins, maxs, node->plane))
110 node = node->children[0];
113 node = node->children[1];
116 if (node->children[0]->contents != CONTENTS_SOLID)
117 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
119 node = node->children[1];
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
128 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
137 return CONTENTS_EMPTY;
139 Mod_CheckLoaded(model);
141 // LordHavoc: modified to start at first clip node,
142 // in other words: first node of the (sub)model
143 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
144 while (node->contents == 0)
145 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
147 return ((mleaf_t *)node)->contents;
151 typedef struct findnonsolidlocationinfo_s
159 findnonsolidlocationinfo_t;
162 extern cvar_t samelevel;
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
166 int i, surfnum, k, *tri, *mark;
167 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
172 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
174 surf = info->model->brushq1.surfaces + *mark;
175 if (surf->flags & SURF_SOLIDCLIP)
178 VectorCopy(surf->plane->normal, surfnormal);
179 if (surf->flags & SURF_PLANEBACK)
180 VectorNegate(surfnormal, surfnormal);
182 for (k = 0;k < surf->mesh.num_triangles;k++)
184 tri = surf->mesh.data_element3i + k * 3;
185 VectorCopy((surf->mesh.data_vertex3f + tri[0] * 3), vert[0]);
186 VectorCopy((surf->mesh.data_vertex3f + tri[1] * 3), vert[1]);
187 VectorCopy((surf->mesh.data_vertex3f + tri[2] * 3), vert[2]);
188 VectorSubtract(vert[1], vert[0], edge[0]);
189 VectorSubtract(vert[2], vert[1], edge[1]);
190 CrossProduct(edge[1], edge[0], facenormal);
191 if (facenormal[0] || facenormal[1] || facenormal[2])
193 VectorNormalize(facenormal);
195 if (VectorDistance(facenormal, surfnormal) > 0.01f)
196 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
198 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
199 if (f <= info->bestdist && f >= -info->bestdist)
201 VectorSubtract(vert[0], vert[2], edge[2]);
202 VectorNormalize(edge[0]);
203 VectorNormalize(edge[1]);
204 VectorNormalize(edge[2]);
205 CrossProduct(facenormal, edge[0], edgenormal[0]);
206 CrossProduct(facenormal, edge[1], edgenormal[1]);
207 CrossProduct(facenormal, edge[2], edgenormal[2]);
209 if (samelevel.integer & 1)
210 VectorNegate(edgenormal[0], edgenormal[0]);
211 if (samelevel.integer & 2)
212 VectorNegate(edgenormal[1], edgenormal[1]);
213 if (samelevel.integer & 4)
214 VectorNegate(edgenormal[2], edgenormal[2]);
215 for (i = 0;i < 3;i++)
216 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
217 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
218 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
219 Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]);
222 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
223 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
224 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
226 // we got lucky, the center is within the face
227 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
231 if (info->bestdist > dist)
233 info->bestdist = dist;
234 VectorScale(facenormal, (info->radius - -dist), info->nudge);
239 if (info->bestdist > dist)
241 info->bestdist = dist;
242 VectorScale(facenormal, (info->radius - dist), info->nudge);
248 // check which edge or vertex the center is nearest
249 for (i = 0;i < 3;i++)
251 f = DotProduct(info->center, edge[i]);
252 if (f >= DotProduct(vert[0], edge[i])
253 && f <= DotProduct(vert[1], edge[i]))
256 VectorMA(info->center, -f, edge[i], point);
257 dist = sqrt(DotProduct(point, point));
258 if (info->bestdist > dist)
260 info->bestdist = dist;
261 VectorScale(point, (info->radius / dist), info->nudge);
263 // skip both vertex checks
264 // (both are further away than this edge)
269 // not on edge, check first vertex of edge
270 VectorSubtract(info->center, vert[i], point);
271 dist = sqrt(DotProduct(point, point));
272 if (info->bestdist > dist)
274 info->bestdist = dist;
275 VectorScale(point, (info->radius / dist), info->nudge);
287 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
291 if (((mleaf_t *)node)->nummarksurfaces)
292 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
296 float f = PlaneDiff(info->center, node->plane);
297 if (f >= -info->bestdist)
298 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
299 if (f <= info->bestdist)
300 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
304 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
307 findnonsolidlocationinfo_t info;
313 VectorCopy(in, info.center);
314 info.radius = radius;
319 VectorClear(info.nudge);
320 info.bestdist = radius;
321 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
322 VectorAdd(info.center, info.nudge, info.center);
324 while (info.bestdist < radius && ++i < 10);
325 VectorCopy(info.center, out);
328 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
330 switch(nativecontents)
335 return SUPERCONTENTS_SOLID;
337 return SUPERCONTENTS_WATER;
339 return SUPERCONTENTS_SLIME;
341 return SUPERCONTENTS_LAVA;
343 return SUPERCONTENTS_SKY;
348 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
350 if (supercontents & SUPERCONTENTS_SOLID)
351 return CONTENTS_SOLID;
352 if (supercontents & SUPERCONTENTS_SKY)
354 if (supercontents & SUPERCONTENTS_LAVA)
355 return CONTENTS_LAVA;
356 if (supercontents & SUPERCONTENTS_SLIME)
357 return CONTENTS_SLIME;
358 if (supercontents & SUPERCONTENTS_WATER)
359 return CONTENTS_WATER;
360 return CONTENTS_EMPTY;
365 // the hull we're tracing through
368 // the trace structure to fill in
371 // start, end, and end - start (in model space)
376 RecursiveHullCheckTraceInfo_t;
378 // 1/32 epsilon to keep floating point happy
379 #define DIST_EPSILON (0.03125)
381 #define HULLCHECKSTATE_EMPTY 0
382 #define HULLCHECKSTATE_SOLID 1
383 #define HULLCHECKSTATE_DONE 2
385 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
387 // status variables, these don't need to be saved on the stack when
388 // recursing... but are because this should be thread-safe
389 // (note: tracing against a bbox is not thread-safe, yet)
394 // variables that need to be stored on the stack when recursing
399 // LordHavoc: a goto! everyone flee in terror... :)
404 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
405 if (!t->trace->startfound)
407 t->trace->startfound = true;
408 t->trace->startsupercontents |= num;
410 if (num & SUPERCONTENTS_LIQUIDSMASK)
411 t->trace->inwater = true;
413 t->trace->inopen = true;
414 if (num & t->trace->hitsupercontentsmask)
416 // if the first leaf is solid, set startsolid
417 if (t->trace->allsolid)
418 t->trace->startsolid = true;
419 return HULLCHECKSTATE_SOLID;
423 t->trace->allsolid = false;
424 return HULLCHECKSTATE_EMPTY;
428 // find the point distances
429 node = t->hull->clipnodes + num;
431 plane = t->hull->planes + node->planenum;
434 t1 = p1[plane->type] - plane->dist;
435 t2 = p2[plane->type] - plane->dist;
439 t1 = DotProduct (plane->normal, p1) - plane->dist;
440 t2 = DotProduct (plane->normal, p2) - plane->dist;
447 num = node->children[1];
456 num = node->children[0];
462 // the line intersects, find intersection point
463 // LordHavoc: this uses the original trace for maximum accuracy
466 t1 = t->start[plane->type] - plane->dist;
467 t2 = t->end[plane->type] - plane->dist;
471 t1 = DotProduct (plane->normal, t->start) - plane->dist;
472 t2 = DotProduct (plane->normal, t->end) - plane->dist;
475 midf = t1 / (t1 - t2);
476 midf = bound(p1f, midf, p2f);
477 VectorMA(t->start, midf, t->dist, mid);
479 // recurse both sides, front side first
480 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
481 // if this side is not empty, return what it is (solid or done)
482 if (ret != HULLCHECKSTATE_EMPTY)
485 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
486 // if other side is not solid, return what it is (empty or done)
487 if (ret != HULLCHECKSTATE_SOLID)
490 // front is air and back is solid, this is the impact point...
493 t->trace->plane.dist = -plane->dist;
494 VectorNegate (plane->normal, t->trace->plane.normal);
498 t->trace->plane.dist = plane->dist;
499 VectorCopy (plane->normal, t->trace->plane.normal);
502 // bias away from surface a bit
503 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
504 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
506 midf = t1 / (t1 - t2);
507 t->trace->fraction = bound(0.0f, midf, 1.0);
509 return HULLCHECKSTATE_DONE;
512 static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
514 // this function currently only supports same size start and end
516 RecursiveHullCheckTraceInfo_t rhc;
518 memset(&rhc, 0, sizeof(rhc));
519 memset(trace, 0, sizeof(trace_t));
521 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
522 rhc.trace->fraction = 1;
523 rhc.trace->allsolid = true;
524 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
526 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
527 else if (model->brush.ishlbsp)
529 if (boxsize[0] <= 32)
531 if (boxsize[2] < 54) // pick the nearest of 36 or 72
532 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
534 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
537 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
541 if (boxsize[0] <= 32)
542 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
544 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
546 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
547 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
548 VectorSubtract(rhc.end, rhc.start, rhc.dist);
549 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
552 static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
554 int side, distz = endz - startz;
559 if (node->contents < 0)
560 return false; // didn't hit anything
562 switch (node->plane->type)
565 node = node->children[x < node->plane->dist];
568 node = node->children[y < node->plane->dist];
571 side = startz < node->plane->dist;
572 if ((endz < node->plane->dist) == side)
574 node = node->children[side];
577 // found an intersection
578 mid = node->plane->dist;
581 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
582 front += startz * node->plane->normal[2];
583 back += endz * node->plane->normal[2];
584 side = front < node->plane->dist;
585 if ((back < node->plane->dist) == side)
587 node = node->children[side];
590 // found an intersection
591 mid = startz + distz * (front - node->plane->dist) / (front - back);
595 // go down front side
596 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
597 return true; // hit something
600 // check for impact on this node
601 if (node->numsurfaces)
606 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
607 for (i = 0;i < node->numsurfaces;i++, surf++)
609 if (!(surf->flags & SURF_LIGHTMAP))
610 continue; // no lightmaps
612 ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3]);
613 dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]);
615 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
618 ds -= surf->texturemins[0];
619 dt -= surf->texturemins[1];
621 if (ds > surf->extents[0] || dt > surf->extents[1])
627 int maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
628 line3 = ((surf->extents[0]>>4)+1)*3;
629 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
631 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
633 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
635 scale = d_lightstylevalue[surf->styles[maps]];
636 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
637 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
638 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
639 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
644 LordHavoc: here's the readable version of the interpolation
645 code, not quite as easy for the compiler to optimize...
647 dsfrac is the X position in the lightmap pixel, * 16
648 dtfrac is the Y position in the lightmap pixel, * 16
649 r00 is top left corner, r01 is top right corner
650 r10 is bottom left corner, r11 is bottom right corner
651 g and b are the same layout.
652 r0 and r1 are the top and bottom intermediate results
654 first we interpolate the top two points, to get the top
657 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
658 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
659 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
661 then we interpolate the bottom two points, to get the
664 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
665 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
666 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
668 then we interpolate the top and bottom samples to get the
669 middle sample (the one which was requested)
671 r = (((r1-r0) * dtfrac) >> 4) + r0;
672 g = (((g1-g0) * dtfrac) >> 4) + g0;
673 b = (((b1-b0) * dtfrac) >> 4) + b0;
676 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
677 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
678 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
680 return true; // success
685 node = node->children[side ^ 1];
687 distz = endz - startz;
692 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
694 Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, cl.worldmodel->brushq1.nodes + cl.worldmodel->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
697 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
704 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
714 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
717 for (c = *in++;c > 0;c--)
721 Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\"\n", loadmodel->name);
730 static void Mod_Q1BSP_LoadTextures(lump_t *l)
732 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
734 texture_t *tx, *tx2, *anims[10], *altanims[10];
736 qbyte *data, *mtdata;
739 loadmodel->brushq1.textures = NULL;
741 // add two slots for notexture walls and notexture liquids
744 m = (dmiptexlump_t *)(mod_base + l->fileofs);
745 m->nummiptex = LittleLong (m->nummiptex);
746 loadmodel->brushq1.numtextures = m->nummiptex + 2;
751 loadmodel->brushq1.numtextures = 2;
754 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
756 // fill out all slots with notexture
757 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
760 strcpy(tx->name, "NO TEXTURE FOUND");
763 tx->skin.base = r_notexture;
764 tx->shader = &Cshader_wall_lightmap;
765 tx->flags = SURF_SOLIDCLIP;
766 if (i == loadmodel->brushq1.numtextures - 1)
768 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
769 tx->shader = &Cshader_water;
771 tx->currentframe = tx;
777 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
779 // LordHavoc: mostly rewritten map texture loader
780 for (i = 0;i < m->nummiptex;i++)
782 dofs[i] = LittleLong(dofs[i]);
783 if (dofs[i] == -1 || r_nosurftextures.integer)
785 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
787 // make sure name is no more than 15 characters
788 for (j = 0;dmiptex->name[j] && j < 15;j++)
789 name[j] = dmiptex->name[j];
792 mtwidth = LittleLong(dmiptex->width);
793 mtheight = LittleLong(dmiptex->height);
795 j = LittleLong(dmiptex->offsets[0]);
799 if (j < 40 || j + mtwidth * mtheight > l->filelen)
801 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
804 mtdata = (qbyte *)dmiptex + j;
807 if ((mtwidth & 15) || (mtheight & 15))
808 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
810 // LordHavoc: force all names to lowercase
811 for (j = 0;name[j];j++)
812 if (name[j] >= 'A' && name[j] <= 'Z')
813 name[j] += 'a' - 'A';
815 tx = loadmodel->brushq1.textures + i;
816 strcpy(tx->name, name);
818 tx->height = mtheight;
822 sprintf(tx->name, "unnamed%i", i);
823 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
826 // LordHavoc: HL sky textures are entirely different than quake
827 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
829 if (loadmodel->isworldmodel)
831 data = loadimagepixels(tx->name, false, 0, 0);
834 if (image_width == 256 && image_height == 128)
842 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
844 R_InitSky(mtdata, 1);
847 else if (mtdata != NULL)
848 R_InitSky(mtdata, 1);
853 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
855 // did not find external texture, load it from the bsp or wad3
856 if (loadmodel->brush.ishlbsp)
858 // internal texture overrides wad
859 qbyte *pixels, *freepixels, *fogpixels;
860 pixels = freepixels = NULL;
862 pixels = W_ConvertWAD3Texture(dmiptex);
864 pixels = freepixels = W_GetTexture(tx->name);
867 tx->width = image_width;
868 tx->height = image_height;
869 tx->skin.base = tx->skin.merged = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
870 if (Image_CheckAlpha(pixels, image_width * image_height, true))
872 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
873 for (j = 0;j < image_width * image_height * 4;j += 4)
875 fogpixels[j + 0] = 255;
876 fogpixels[j + 1] = 255;
877 fogpixels[j + 2] = 255;
878 fogpixels[j + 3] = pixels[j + 3];
880 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
885 Mem_Free(freepixels);
887 else if (mtdata) // texture included
888 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
891 if (tx->skin.base == NULL)
896 tx->skin.base = r_notexture;
899 if (tx->name[0] == '*')
901 // turb does not block movement
902 tx->flags &= ~SURF_SOLIDCLIP;
903 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
904 // LordHavoc: some turbulent textures should be fullbright and solid
905 if (!strncmp(tx->name,"*lava",5)
906 || !strncmp(tx->name,"*teleport",9)
907 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
908 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
910 tx->flags |= SURF_WATERALPHA;
911 tx->shader = &Cshader_water;
913 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
915 tx->flags |= SURF_DRAWSKY;
916 tx->shader = &Cshader_sky;
920 tx->flags |= SURF_LIGHTMAP;
922 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
923 tx->shader = &Cshader_wall_lightmap;
926 // start out with no animation
927 tx->currentframe = tx;
930 // sequence the animations
931 for (i = 0;i < m->nummiptex;i++)
933 tx = loadmodel->brushq1.textures + i;
934 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
936 if (tx->anim_total[0] || tx->anim_total[1])
937 continue; // already sequenced
939 // find the number of frames in the animation
940 memset(anims, 0, sizeof(anims));
941 memset(altanims, 0, sizeof(altanims));
943 for (j = i;j < m->nummiptex;j++)
945 tx2 = loadmodel->brushq1.textures + j;
946 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
950 if (num >= '0' && num <= '9')
951 anims[num - '0'] = tx2;
952 else if (num >= 'a' && num <= 'j')
953 altanims[num - 'a'] = tx2;
955 Con_Printf("Bad animating texture %s\n", tx->name);
959 for (j = 0;j < 10;j++)
966 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
969 for (j = 0;j < max;j++)
973 Con_Printf("Missing frame %i of %s\n", j, tx->name);
977 for (j = 0;j < altmax;j++)
981 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
990 // if there is no alternate animation, duplicate the primary
991 // animation into the alternate
993 for (k = 0;k < 10;k++)
994 altanims[k] = anims[k];
997 // link together the primary animation
998 for (j = 0;j < max;j++)
1001 tx2->animated = true;
1002 tx2->anim_total[0] = max;
1003 tx2->anim_total[1] = altmax;
1004 for (k = 0;k < 10;k++)
1006 tx2->anim_frames[0][k] = anims[k];
1007 tx2->anim_frames[1][k] = altanims[k];
1011 // if there really is an alternate anim...
1012 if (anims[0] != altanims[0])
1014 // link together the alternate animation
1015 for (j = 0;j < altmax;j++)
1018 tx2->animated = true;
1019 // the primary/alternate are reversed here
1020 tx2->anim_total[0] = altmax;
1021 tx2->anim_total[1] = max;
1022 for (k = 0;k < 10;k++)
1024 tx2->anim_frames[0][k] = altanims[k];
1025 tx2->anim_frames[1][k] = anims[k];
1032 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1035 qbyte *in, *out, *data, d;
1036 char litfilename[1024];
1037 loadmodel->brushq1.lightdata = NULL;
1038 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1040 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1041 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1043 else // LordHavoc: bsp version 29 (normal white lighting)
1045 // LordHavoc: hope is not lost yet, check for a .lit file to load
1046 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1047 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1048 strlcat (litfilename, ".lit", sizeof (litfilename));
1049 data = (qbyte*) FS_LoadFile(litfilename, false);
1052 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1054 i = LittleLong(((int *)data)[1]);
1057 Con_DPrintf("loaded %s\n", litfilename);
1058 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1059 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1065 Con_Printf("Unknown .lit file version (%d)\n", i);
1071 if (fs_filesize == 8)
1072 Con_Printf("Empty .lit file, ignoring\n");
1074 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1078 // LordHavoc: oh well, expand the white lighting data
1081 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1082 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1083 out = loadmodel->brushq1.lightdata;
1084 memcpy(in, mod_base + l->fileofs, l->filelen);
1085 for (i = 0;i < l->filelen;i++)
1095 static void Mod_Q1BSP_LoadLightList(void)
1097 int a, n, numlights;
1098 char lightsfilename[1024], *s, *t, *lightsstring;
1101 strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1102 FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1103 strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1104 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1110 while (*s && *s != '\n')
1114 Mem_Free(lightsstring);
1115 Host_Error("lights file must end with a newline\n");
1120 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1123 while (*s && n < numlights)
1126 while (*s && *s != '\n')
1130 Mem_Free(lightsstring);
1131 Host_Error("misparsed lights file!\n");
1133 e = loadmodel->brushq1.lights + n;
1135 a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
1139 Mem_Free(lightsstring);
1140 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
1147 Mem_Free(lightsstring);
1148 Host_Error("misparsed lights file!\n");
1150 loadmodel->brushq1.numlights = numlights;
1151 Mem_Free(lightsstring);
1155 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1157 loadmodel->brushq1.num_compressedpvs = 0;
1158 loadmodel->brushq1.data_compressedpvs = NULL;
1161 loadmodel->brushq1.num_compressedpvs = l->filelen;
1162 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1163 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1166 // used only for HalfLife maps
1167 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1169 char key[128], value[4096];
1174 if (!COM_ParseToken(&data, false))
1176 if (com_token[0] != '{')
1180 if (!COM_ParseToken(&data, false))
1182 if (com_token[0] == '}')
1183 break; // end of worldspawn
1184 if (com_token[0] == '_')
1185 strcpy(key, com_token + 1);
1187 strcpy(key, com_token);
1188 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1189 key[strlen(key)-1] = 0;
1190 if (!COM_ParseToken(&data, false))
1192 strcpy(value, com_token);
1193 if (!strcmp("wad", key)) // for HalfLife maps
1195 if (loadmodel->brush.ishlbsp)
1198 for (i = 0;i < 4096;i++)
1199 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1205 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1206 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1208 else if (value[i] == ';' || value[i] == 0)
1212 strcpy(wadname, "textures/");
1213 strcat(wadname, &value[j]);
1214 W_LoadTextureWadFile(wadname, false);
1226 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1228 loadmodel->brush.entities = NULL;
1231 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1232 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1233 if (loadmodel->brush.ishlbsp)
1234 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1238 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1244 in = (void *)(mod_base + l->fileofs);
1245 if (l->filelen % sizeof(*in))
1246 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1247 count = l->filelen / sizeof(*in);
1248 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1250 loadmodel->brushq1.vertexes = out;
1251 loadmodel->brushq1.numvertexes = count;
1253 for ( i=0 ; i<count ; i++, in++, out++)
1255 out->position[0] = LittleFloat(in->point[0]);
1256 out->position[1] = LittleFloat(in->point[1]);
1257 out->position[2] = LittleFloat(in->point[2]);
1261 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1267 in = (void *)(mod_base + l->fileofs);
1268 if (l->filelen % sizeof(*in))
1269 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1270 count = l->filelen / sizeof(*in);
1271 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1273 loadmodel->brushq1.submodels = out;
1274 loadmodel->brush.numsubmodels = count;
1276 for ( i=0 ; i<count ; i++, in++, out++)
1278 for (j=0 ; j<3 ; j++)
1280 // spread the mins / maxs by a pixel
1281 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1282 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1283 out->origin[j] = LittleFloat(in->origin[j]);
1285 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1286 out->headnode[j] = LittleLong(in->headnode[j]);
1287 out->visleafs = LittleLong(in->visleafs);
1288 out->firstface = LittleLong(in->firstface);
1289 out->numfaces = LittleLong(in->numfaces);
1293 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1299 in = (void *)(mod_base + l->fileofs);
1300 if (l->filelen % sizeof(*in))
1301 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1302 count = l->filelen / sizeof(*in);
1303 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1305 loadmodel->brushq1.edges = out;
1306 loadmodel->brushq1.numedges = count;
1308 for ( i=0 ; i<count ; i++, in++, out++)
1310 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1311 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1315 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1319 int i, j, k, count, miptex;
1321 in = (void *)(mod_base + l->fileofs);
1322 if (l->filelen % sizeof(*in))
1323 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1324 count = l->filelen / sizeof(*in);
1325 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1327 loadmodel->brushq1.texinfo = out;
1328 loadmodel->brushq1.numtexinfo = count;
1330 for (i = 0;i < count;i++, in++, out++)
1332 for (k = 0;k < 2;k++)
1333 for (j = 0;j < 4;j++)
1334 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1336 miptex = LittleLong(in->miptex);
1337 out->flags = LittleLong(in->flags);
1339 out->texture = NULL;
1340 if (loadmodel->brushq1.textures)
1342 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1343 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1345 out->texture = loadmodel->brushq1.textures + miptex;
1347 if (out->flags & TEX_SPECIAL)
1349 // if texture chosen is NULL or the shader needs a lightmap,
1350 // force to notexture water shader
1351 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1352 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1356 // if texture chosen is NULL, force to notexture
1357 if (out->texture == NULL)
1358 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1364 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1369 mins[0] = mins[1] = mins[2] = 9999;
1370 maxs[0] = maxs[1] = maxs[2] = -9999;
1372 for (i = 0;i < numverts;i++)
1374 for (j = 0;j < 3;j++, v++)
1384 #define MAX_SUBDIVPOLYTRIANGLES 4096
1385 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1387 static int subdivpolyverts, subdivpolytriangles;
1388 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1389 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1391 static int subdivpolylookupvert(vec3_t v)
1394 for (i = 0;i < subdivpolyverts;i++)
1395 if (subdivpolyvert[i][0] == v[0]
1396 && subdivpolyvert[i][1] == v[1]
1397 && subdivpolyvert[i][2] == v[2])
1399 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1400 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1401 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1402 return subdivpolyverts++;
1405 static void SubdividePolygon(int numverts, float *verts)
1407 int i, i1, i2, i3, f, b, c, p;
1408 vec3_t mins, maxs, front[256], back[256];
1409 float m, *pv, *cv, dist[256], frac;
1412 Host_Error("SubdividePolygon: ran out of verts in buffer");
1414 BoundPoly(numverts, verts, mins, maxs);
1416 for (i = 0;i < 3;i++)
1418 m = (mins[i] + maxs[i]) * 0.5;
1419 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1420 if (maxs[i] - m < 8)
1422 if (m - mins[i] < 8)
1426 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1427 dist[c] = cv[i] - m;
1430 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1434 VectorCopy(pv, front[f]);
1439 VectorCopy(pv, back[b]);
1442 if (dist[p] == 0 || dist[c] == 0)
1444 if ((dist[p] > 0) != (dist[c] > 0) )
1447 frac = dist[p] / (dist[p] - dist[c]);
1448 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1449 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1450 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1456 SubdividePolygon(f, front[0]);
1457 SubdividePolygon(b, back[0]);
1461 i1 = subdivpolylookupvert(verts);
1462 i2 = subdivpolylookupvert(verts + 3);
1463 for (i = 2;i < numverts;i++)
1465 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1467 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1471 i3 = subdivpolylookupvert(verts + i * 3);
1472 subdivpolyindex[subdivpolytriangles][0] = i1;
1473 subdivpolyindex[subdivpolytriangles][1] = i2;
1474 subdivpolyindex[subdivpolytriangles][2] = i3;
1476 subdivpolytriangles++;
1480 //Breaks a polygon up along axial 64 unit
1481 //boundaries so that turbulent and sky warps
1482 //can be done reasonably.
1483 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1489 subdivpolytriangles = 0;
1490 subdivpolyverts = 0;
1491 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1492 if (subdivpolytriangles < 1)
1493 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1495 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1496 mesh->num_vertices = subdivpolyverts;
1497 mesh->num_triangles = subdivpolytriangles;
1498 mesh->vertex = (surfvertex_t *)(mesh + 1);
1499 mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1500 memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1502 for (i = 0;i < mesh->num_triangles;i++)
1503 for (j = 0;j < 3;j++)
1504 mesh->index[i*3+j] = subdivpolyindex[i][j];
1506 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1508 VectorCopy(subdivpolyvert[i], v->v);
1509 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1510 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1515 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1518 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1519 mesh->num_vertices = numverts;
1520 mesh->num_triangles = numtriangles;
1521 mesh->data_vertex3f = (float *)(mesh + 1);
1522 mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1523 mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1524 mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1525 mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1526 mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1527 mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1528 mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1529 mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1530 mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1534 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1537 float *vec, *vert, mins[3], maxs[3], val, *v;
1540 // convert edges back to a normal polygon
1541 surf->poly_numverts = numedges;
1542 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1543 for (i = 0;i < numedges;i++)
1545 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1547 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1549 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1550 VectorCopy(vec, vert);
1554 // calculate polygon bounding box and center
1555 vert = surf->poly_verts;
1556 VectorCopy(vert, mins);
1557 VectorCopy(vert, maxs);
1559 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1561 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1562 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1563 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1565 VectorCopy(mins, surf->poly_mins);
1566 VectorCopy(maxs, surf->poly_maxs);
1567 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1568 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1569 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1571 // generate surface extents information
1572 tex = surf->texinfo;
1573 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1574 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1575 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1577 for (j = 0;j < 2;j++)
1579 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1586 for (i = 0;i < 2;i++)
1588 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1589 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1593 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1597 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1601 in = (void *)(mod_base + l->fileofs);
1602 if (l->filelen % sizeof(*in))
1603 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1604 count = l->filelen / sizeof(*in);
1605 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1607 loadmodel->brushq1.numsurfaces = count;
1608 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1609 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1610 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1612 for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, in++, surf++)
1614 surf->number = surfnum;
1615 // FIXME: validate edges, texinfo, etc?
1616 firstedge = LittleLong(in->firstedge);
1617 numedges = LittleShort(in->numedges);
1618 if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges)
1619 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1620 i = LittleShort(in->texinfo);
1621 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1622 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1623 surf->texinfo = loadmodel->brushq1.texinfo + i;
1624 surf->flags = surf->texinfo->texture->flags;
1626 planenum = LittleShort(in->planenum);
1627 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1628 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1630 if (LittleShort(in->side))
1631 surf->flags |= SURF_PLANEBACK;
1633 surf->plane = loadmodel->brushq1.planes + planenum;
1635 // clear lightmap (filled in later)
1636 surf->lightmaptexture = NULL;
1638 // force lightmap upload on first time seeing the surface
1639 surf->cached_dlight = true;
1641 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1643 ssize = (surf->extents[0] >> 4) + 1;
1644 tsize = (surf->extents[1] >> 4) + 1;
1647 for (i = 0;i < MAXLIGHTMAPS;i++)
1648 surf->styles[i] = in->styles[i];
1649 i = LittleLong(in->lightofs);
1651 surf->samples = NULL;
1652 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1653 surf->samples = loadmodel->brushq1.lightdata + i;
1654 else // LordHavoc: white lighting (bsp version 29)
1655 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1657 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1659 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1660 Host_Error("Bad surface extents");
1661 // stainmap for permanent marks on walls
1662 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1664 memset(surf->stainsamples, 255, ssize * tsize * 3);
1668 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1670 for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++)
1673 mesh->num_vertices = surf->poly_numverts;
1674 mesh->num_triangles = surf->poly_numverts - 2;
1675 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1676 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1677 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1678 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1679 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1680 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1681 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1682 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1683 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1684 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1686 surf->lightmaptexturestride = 0;
1687 surf->lightmaptexture = NULL;
1689 for (i = 0;i < mesh->num_vertices;i++)
1691 mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1692 mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1693 mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1694 s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1695 t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1696 mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1697 mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1698 mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1699 mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1700 mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1701 mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1702 mesh->data_lightmapoffsets[i] = 0;
1705 for (i = 0;i < mesh->num_triangles;i++)
1707 mesh->data_element3i[i * 3 + 0] = 0;
1708 mesh->data_element3i[i * 3 + 1] = i + 1;
1709 mesh->data_element3i[i * 3 + 2] = i + 2;
1712 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1713 Mod_BuildTextureVectorsAndNormals(mesh->num_vertices, mesh->num_triangles, mesh->data_vertex3f, mesh->data_texcoordtexture2f, mesh->data_element3i, mesh->data_svector3f, mesh->data_tvector3f, mesh->data_normal3f);
1715 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1717 int i, iu, iv, smax, tmax;
1718 float u, v, ubase, vbase, uscale, vscale;
1720 smax = surf->extents[0] >> 4;
1721 tmax = surf->extents[1] >> 4;
1723 surf->flags |= SURF_LIGHTMAP;
1724 if (r_miplightmaps.integer)
1726 surf->lightmaptexturestride = smax+1;
1727 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1731 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1732 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1734 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1735 uscale = (uscale - ubase) / (smax + 1);
1736 vscale = (vscale - vbase) / (tmax + 1);
1738 for (i = 0;i < mesh->num_vertices;i++)
1740 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1741 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1742 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1743 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1744 // LordHavoc: calc lightmap data offset for vertex lighting to use
1747 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1753 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1755 node->parent = parent;
1756 if (node->contents < 0)
1758 Mod_Q1BSP_SetParent(node->children[0], node);
1759 Mod_Q1BSP_SetParent(node->children[1], node);
1762 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1768 in = (void *)(mod_base + l->fileofs);
1769 if (l->filelen % sizeof(*in))
1770 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1771 count = l->filelen / sizeof(*in);
1772 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1774 loadmodel->brushq1.nodes = out;
1775 loadmodel->brushq1.numnodes = count;
1777 for ( i=0 ; i<count ; i++, in++, out++)
1779 for (j=0 ; j<3 ; j++)
1781 out->mins[j] = LittleShort(in->mins[j]);
1782 out->maxs[j] = LittleShort(in->maxs[j]);
1785 p = LittleLong(in->planenum);
1786 out->plane = loadmodel->brushq1.planes + p;
1788 out->firstsurface = LittleShort(in->firstface);
1789 out->numsurfaces = LittleShort(in->numfaces);
1791 for (j=0 ; j<2 ; j++)
1793 p = LittleShort(in->children[j]);
1795 out->children[j] = loadmodel->brushq1.nodes + p;
1797 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1801 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1804 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1808 int i, j, count, p, pvschainbytes;
1811 in = (void *)(mod_base + l->fileofs);
1812 if (l->filelen % sizeof(*in))
1813 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1814 count = l->filelen / sizeof(*in);
1815 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1817 loadmodel->brushq1.leafs = out;
1818 loadmodel->brushq1.numleafs = count;
1819 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1820 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1822 for ( i=0 ; i<count ; i++, in++, out++)
1824 for (j=0 ; j<3 ; j++)
1826 out->mins[j] = LittleShort(in->mins[j]);
1827 out->maxs[j] = LittleShort(in->maxs[j]);
1830 // FIXME: this function could really benefit from some error checking
1832 out->contents = LittleLong(in->contents);
1834 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1835 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1836 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
1838 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid marksurface range %i:%i outside range %i:%i\n", out->firstmarksurface, out->firstmarksurface + out->nummarksurfaces, 0, loadmodel->brushq1.nummarksurfaces);
1839 out->firstmarksurface = NULL;
1840 out->nummarksurfaces = 0;
1844 memset(out->pvsdata, 0xFF, pvschainbytes);
1845 pvs += pvschainbytes;
1847 p = LittleLong(in->visofs);
1850 if (p >= loadmodel->brushq1.num_compressedpvs)
1851 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
1853 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1856 for (j = 0;j < 4;j++)
1857 out->ambient_sound_level[j] = in->ambient_level[j];
1859 // FIXME: Insert caustics here
1863 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1865 dclipnode_t *in, *out;
1869 in = (void *)(mod_base + l->fileofs);
1870 if (l->filelen % sizeof(*in))
1871 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1872 count = l->filelen / sizeof(*in);
1873 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1875 loadmodel->brushq1.clipnodes = out;
1876 loadmodel->brushq1.numclipnodes = count;
1878 if (loadmodel->brush.ishlbsp)
1880 hull = &loadmodel->brushq1.hulls[1];
1881 hull->clipnodes = out;
1882 hull->firstclipnode = 0;
1883 hull->lastclipnode = count-1;
1884 hull->planes = loadmodel->brushq1.planes;
1885 hull->clip_mins[0] = -16;
1886 hull->clip_mins[1] = -16;
1887 hull->clip_mins[2] = -36;
1888 hull->clip_maxs[0] = 16;
1889 hull->clip_maxs[1] = 16;
1890 hull->clip_maxs[2] = 36;
1891 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1893 hull = &loadmodel->brushq1.hulls[2];
1894 hull->clipnodes = out;
1895 hull->firstclipnode = 0;
1896 hull->lastclipnode = count-1;
1897 hull->planes = loadmodel->brushq1.planes;
1898 hull->clip_mins[0] = -32;
1899 hull->clip_mins[1] = -32;
1900 hull->clip_mins[2] = -32;
1901 hull->clip_maxs[0] = 32;
1902 hull->clip_maxs[1] = 32;
1903 hull->clip_maxs[2] = 32;
1904 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1906 hull = &loadmodel->brushq1.hulls[3];
1907 hull->clipnodes = out;
1908 hull->firstclipnode = 0;
1909 hull->lastclipnode = count-1;
1910 hull->planes = loadmodel->brushq1.planes;
1911 hull->clip_mins[0] = -16;
1912 hull->clip_mins[1] = -16;
1913 hull->clip_mins[2] = -18;
1914 hull->clip_maxs[0] = 16;
1915 hull->clip_maxs[1] = 16;
1916 hull->clip_maxs[2] = 18;
1917 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1921 hull = &loadmodel->brushq1.hulls[1];
1922 hull->clipnodes = out;
1923 hull->firstclipnode = 0;
1924 hull->lastclipnode = count-1;
1925 hull->planes = loadmodel->brushq1.planes;
1926 hull->clip_mins[0] = -16;
1927 hull->clip_mins[1] = -16;
1928 hull->clip_mins[2] = -24;
1929 hull->clip_maxs[0] = 16;
1930 hull->clip_maxs[1] = 16;
1931 hull->clip_maxs[2] = 32;
1932 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1934 hull = &loadmodel->brushq1.hulls[2];
1935 hull->clipnodes = out;
1936 hull->firstclipnode = 0;
1937 hull->lastclipnode = count-1;
1938 hull->planes = loadmodel->brushq1.planes;
1939 hull->clip_mins[0] = -32;
1940 hull->clip_mins[1] = -32;
1941 hull->clip_mins[2] = -24;
1942 hull->clip_maxs[0] = 32;
1943 hull->clip_maxs[1] = 32;
1944 hull->clip_maxs[2] = 64;
1945 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1948 for (i=0 ; i<count ; i++, out++, in++)
1950 out->planenum = LittleLong(in->planenum);
1951 out->children[0] = LittleShort(in->children[0]);
1952 out->children[1] = LittleShort(in->children[1]);
1953 if (out->children[0] >= count || out->children[1] >= count)
1954 Host_Error("Corrupt clipping hull(out of range child)\n");
1958 //Duplicate the drawing hull structure as a clipping hull
1959 static void Mod_Q1BSP_MakeHull0(void)
1966 hull = &loadmodel->brushq1.hulls[0];
1968 in = loadmodel->brushq1.nodes;
1969 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1971 hull->clipnodes = out;
1972 hull->firstclipnode = 0;
1973 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1974 hull->planes = loadmodel->brushq1.planes;
1976 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1978 out->planenum = in->plane - loadmodel->brushq1.planes;
1979 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1980 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1984 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1989 in = (void *)(mod_base + l->fileofs);
1990 if (l->filelen % sizeof(*in))
1991 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1992 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1993 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1995 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1997 j = (unsigned) LittleShort(in[i]);
1998 if (j >= loadmodel->brushq1.numsurfaces)
1999 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2000 loadmodel->brushq1.marksurfaces[i] = j;
2004 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2009 in = (void *)(mod_base + l->fileofs);
2010 if (l->filelen % sizeof(*in))
2011 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2012 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2013 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2015 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2016 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2020 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2026 in = (void *)(mod_base + l->fileofs);
2027 if (l->filelen % sizeof(*in))
2028 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2030 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2031 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2033 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2035 out->normal[0] = LittleFloat(in->normal[0]);
2036 out->normal[1] = LittleFloat(in->normal[1]);
2037 out->normal[2] = LittleFloat(in->normal[2]);
2038 out->dist = LittleFloat(in->dist);
2044 typedef struct portal_s
2047 mnode_t *nodes[2]; // [0] = front side of plane
2048 struct portal_s *next[2];
2050 struct portal_s *chain; // all portals are linked into a list
2054 static portal_t *portalchain;
2061 static portal_t *AllocPortal(void)
2064 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2065 p->chain = portalchain;
2070 static void FreePortal(portal_t *p)
2075 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2077 // calculate children first
2078 if (node->children[0]->contents >= 0)
2079 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2080 if (node->children[1]->contents >= 0)
2081 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2083 // make combined bounding box from children
2084 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2085 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2086 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2087 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2088 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2089 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2092 static void Mod_Q1BSP_FinalizePortals(void)
2094 int i, j, numportals, numpoints;
2095 portal_t *p, *pnext;
2098 mleaf_t *leaf, *endleaf;
2101 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2102 leaf = loadmodel->brushq1.leafs;
2103 endleaf = leaf + loadmodel->brushq1.numleafs;
2104 for (;leaf < endleaf;leaf++)
2106 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2107 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2114 for (i = 0;i < 2;i++)
2116 leaf = (mleaf_t *)p->nodes[i];
2118 for (j = 0;j < w->numpoints;j++)
2120 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2121 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2122 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2123 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2124 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2125 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2132 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2134 // tally up portal and point counts
2140 // note: this check must match the one below or it will usually corrupt memory
2141 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2142 if (p->winding && p->nodes[0] != p->nodes[1]
2143 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2144 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2147 numpoints += p->winding->numpoints * 2;
2151 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2152 loadmodel->brushq1.numportals = numportals;
2153 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2154 loadmodel->brushq1.numportalpoints = numpoints;
2155 // clear all leaf portal chains
2156 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2157 loadmodel->brushq1.leafs[i].portals = NULL;
2158 // process all portals in the global portal chain, while freeing them
2159 portal = loadmodel->brushq1.portals;
2160 point = loadmodel->brushq1.portalpoints;
2169 // note: this check must match the one above or it will usually corrupt memory
2170 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2171 if (p->nodes[0] != p->nodes[1]
2172 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2173 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2175 // first make the back to front portal(forward portal)
2176 portal->points = point;
2177 portal->numpoints = p->winding->numpoints;
2178 portal->plane.dist = p->plane.dist;
2179 VectorCopy(p->plane.normal, portal->plane.normal);
2180 portal->here = (mleaf_t *)p->nodes[1];
2181 portal->past = (mleaf_t *)p->nodes[0];
2183 for (j = 0;j < portal->numpoints;j++)
2185 VectorCopy(p->winding->points[j], point->position);
2188 PlaneClassify(&portal->plane);
2190 // link into leaf's portal chain
2191 portal->next = portal->here->portals;
2192 portal->here->portals = portal;
2194 // advance to next portal
2197 // then make the front to back portal(backward portal)
2198 portal->points = point;
2199 portal->numpoints = p->winding->numpoints;
2200 portal->plane.dist = -p->plane.dist;
2201 VectorNegate(p->plane.normal, portal->plane.normal);
2202 portal->here = (mleaf_t *)p->nodes[0];
2203 portal->past = (mleaf_t *)p->nodes[1];
2205 for (j = portal->numpoints - 1;j >= 0;j--)
2207 VectorCopy(p->winding->points[j], point->position);
2210 PlaneClassify(&portal->plane);
2212 // link into leaf's portal chain
2213 portal->next = portal->here->portals;
2214 portal->here->portals = portal;
2216 // advance to next portal
2219 Winding_Free(p->winding);
2231 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2234 Host_Error("AddPortalToNodes: NULL front node");
2236 Host_Error("AddPortalToNodes: NULL back node");
2237 if (p->nodes[0] || p->nodes[1])
2238 Host_Error("AddPortalToNodes: already included");
2239 // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
2241 p->nodes[0] = front;
2242 p->next[0] = (portal_t *)front->portals;
2243 front->portals = (mportal_t *)p;
2246 p->next[1] = (portal_t *)back->portals;
2247 back->portals = (mportal_t *)p;
2252 RemovePortalFromNode
2255 static void RemovePortalFromNodes(portal_t *portal)
2259 void **portalpointer;
2261 for (i = 0;i < 2;i++)
2263 node = portal->nodes[i];
2265 portalpointer = (void **) &node->portals;
2270 Host_Error("RemovePortalFromNodes: portal not in leaf");
2274 if (portal->nodes[0] == node)
2276 *portalpointer = portal->next[0];
2277 portal->nodes[0] = NULL;
2279 else if (portal->nodes[1] == node)
2281 *portalpointer = portal->next[1];
2282 portal->nodes[1] = NULL;
2285 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2289 if (t->nodes[0] == node)
2290 portalpointer = (void **) &t->next[0];
2291 else if (t->nodes[1] == node)
2292 portalpointer = (void **) &t->next[1];
2294 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2299 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2302 mnode_t *front, *back, *other_node;
2303 mplane_t clipplane, *plane;
2304 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2305 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2307 // if a leaf, we're done
2311 plane = node->plane;
2313 front = node->children[0];
2314 back = node->children[1];
2316 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2318 // create the new portal by generating a polygon for the node plane,
2319 // and clipping it by all of the other portals(which came from nodes above this one)
2320 nodeportal = AllocPortal();
2321 nodeportal->plane = *plane;
2323 nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2324 side = 0; // shut up compiler warning
2325 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2327 clipplane = portal->plane;
2328 if (portal->nodes[0] == portal->nodes[1])
2329 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2330 if (portal->nodes[0] == node)
2332 else if (portal->nodes[1] == node)
2334 clipplane.dist = -clipplane.dist;
2335 VectorNegate(clipplane.normal, clipplane.normal);
2339 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2341 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2342 if (!nodeportalwinding)
2344 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2349 if (nodeportalwinding)
2351 // if the plane was not clipped on all sides, there was an error
2352 nodeportal->winding = nodeportalwinding;
2353 AddPortalToNodes(nodeportal, front, back);
2356 // split the portals of this node along this node's plane and assign them to the children of this node
2357 // (migrating the portals downward through the tree)
2358 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2360 if (portal->nodes[0] == portal->nodes[1])
2361 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2362 if (portal->nodes[0] == node)
2364 else if (portal->nodes[1] == node)
2367 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2368 nextportal = portal->next[side];
2370 other_node = portal->nodes[!side];
2371 RemovePortalFromNodes(portal);
2373 // cut the portal into two portals, one on each side of the node plane
2374 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2379 AddPortalToNodes(portal, back, other_node);
2381 AddPortalToNodes(portal, other_node, back);
2387 AddPortalToNodes(portal, front, other_node);
2389 AddPortalToNodes(portal, other_node, front);
2393 // the winding is split
2394 splitportal = AllocPortal();
2395 temp = splitportal->chain;
2396 *splitportal = *portal;
2397 splitportal->chain = temp;
2398 splitportal->winding = backwinding;
2399 Winding_Free(portal->winding);
2400 portal->winding = frontwinding;
2404 AddPortalToNodes(portal, front, other_node);
2405 AddPortalToNodes(splitportal, back, other_node);
2409 AddPortalToNodes(portal, other_node, front);
2410 AddPortalToNodes(splitportal, other_node, back);
2414 Mod_Q1BSP_RecursiveNodePortals(front);
2415 Mod_Q1BSP_RecursiveNodePortals(back);
2418 static void Mod_Q1BSP_MakePortals(void)
2421 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2422 Mod_Q1BSP_FinalizePortals();
2425 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2428 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2429 msurface_t *surf, *s;
2430 float *v0, *v1, *v2, *v3;
2431 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2432 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2433 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2435 for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
2437 if (surf->neighborsurfaces[vertnum])
2439 surf->neighborsurfaces[vertnum] = NULL;
2440 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2442 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2443 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2444 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2447 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2448 if (s->neighborsurfaces[vnum] == surf)
2450 if (vnum < s->poly_numverts)
2452 for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
2454 if (s->neighborsurfaces[vnum] == NULL
2455 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2456 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2458 surf->neighborsurfaces[vertnum] = s;
2459 s->neighborsurfaces[vnum] = surf;
2463 if (vnum < s->poly_numverts)
2471 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2473 int i, j, stylecounts[256], totalcount, remapstyles[256];
2475 memset(stylecounts, 0, sizeof(stylecounts));
2476 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2478 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2479 for (j = 0;j < MAXLIGHTMAPS;j++)
2480 stylecounts[surf->styles[j]]++;
2483 model->brushq1.light_styles = 0;
2484 for (i = 0;i < 255;i++)
2488 remapstyles[i] = model->brushq1.light_styles++;
2489 totalcount += stylecounts[i] + 1;
2494 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2495 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2496 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2497 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2498 model->brushq1.light_styles = 0;
2499 for (i = 0;i < 255;i++)
2501 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2503 for (i = 0;i < model->brushq1.light_styles;i++)
2505 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2506 j += stylecounts[model->brushq1.light_style[i]] + 1;
2508 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2510 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2511 for (j = 0;j < MAXLIGHTMAPS;j++)
2512 if (surf->styles[j] != 255)
2513 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2516 for (i = 0;i < model->brushq1.light_styles;i++)
2518 *model->brushq1.light_styleupdatechains[i] = NULL;
2519 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2520 j += stylecounts[model->brushq1.light_style[i]] + 1;
2524 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2527 for (i = 0;i < model->brushq1.numtextures;i++)
2528 model->brushq1.pvstexturechainslength[i] = 0;
2529 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2531 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2533 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2534 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2537 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2539 if (model->brushq1.pvstexturechainslength[i])
2541 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2542 j += model->brushq1.pvstexturechainslength[i] + 1;
2545 model->brushq1.pvstexturechains[i] = NULL;
2547 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2548 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2549 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2550 for (i = 0;i < model->brushq1.numtextures;i++)
2552 if (model->brushq1.pvstexturechainslength[i])
2554 *model->brushq1.pvstexturechains[i] = NULL;
2555 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2560 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2565 while (node->contents >= 0)
2567 d = PlaneDiff(org, node->plane);
2569 node = node->children[0];
2570 else if (d < -radius)
2571 node = node->children[1];
2574 // go down both sides
2575 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2576 node = node->children[1];
2580 // if this is a leaf, accumulate the pvs bits
2581 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2582 for (i = 0;i < pvsbytes;i++)
2583 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2586 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2587 //of the given point.
2588 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2590 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2591 bytes = min(bytes, pvsbufferlength);
2592 memset(pvsbuffer, 0, bytes);
2593 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2597 //Returns PVS data for a given point
2598 //(note: always returns valid data, never NULL)
2599 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2602 Mod_CheckLoaded(model);
2603 // LordHavoc: modified to start at first clip node,
2604 // in other words: first node of the (sub)model
2605 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2606 while (node->contents == 0)
2607 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2608 return ((mleaf_t *)node)->pvsdata;
2611 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2616 VectorSubtract(inmaxs, inmins, size);
2617 if (cmodel->brush.ishlbsp)
2620 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2621 else if (size[0] <= 32)
2623 if (size[2] < 54) // pick the nearest of 36 or 72
2624 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2626 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2629 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2634 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2635 else if (size[0] <= 32)
2636 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2638 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2640 VectorCopy(inmins, outmins);
2641 VectorAdd(inmins, hull->clip_size, outmaxs);
2644 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2645 extern void R_Model_Brush_Draw(entity_render_t *ent);
2646 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2647 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
2648 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2653 mempool_t *mainmempool;
2655 model_t *originalloadmodel;
2656 float dist, modelyawradius, modelradius, *vec;
2659 mod->type = mod_brush;
2661 header = (dheader_t *)buffer;
2663 i = LittleLong(header->version);
2664 if (i != BSPVERSION && i != 30)
2665 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2666 mod->brush.ishlbsp = i == 30;
2668 mod->soundfromcenter = true;
2669 mod->TraceBox = Mod_Q1BSP_TraceBox;
2670 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2671 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2672 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2673 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2674 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2675 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2676 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2677 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2678 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2679 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2680 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2682 if (loadmodel->isworldmodel)
2684 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2685 // until we get a texture for it...
2689 // swap all the lumps
2690 mod_base = (qbyte *)header;
2692 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2693 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2697 // store which lightmap format to use
2698 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2700 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2701 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2702 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2703 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2704 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2705 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2706 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2707 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2708 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2709 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2710 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2711 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2712 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2713 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2714 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2716 if (mod->brushq1.data_compressedpvs)
2717 Mem_Free(mod->brushq1.data_compressedpvs);
2718 mod->brushq1.data_compressedpvs = NULL;
2719 mod->brushq1.num_compressedpvs = 0;
2721 Mod_Q1BSP_MakeHull0();
2722 Mod_Q1BSP_MakePortals();
2724 if (developer.integer)
2725 Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.numleafs - 1, loadmodel->brushq1.numportals);
2727 mod->numframes = 2; // regular and alternate animation
2729 mainmempool = mod->mempool;
2730 loadname = mod->name;
2732 Mod_Q1BSP_LoadLightList();
2733 originalloadmodel = loadmodel;
2736 // set up the submodels(FIXME: this is confusing)
2738 for (i = 0;i < mod->brush.numsubmodels;i++)
2740 bm = &mod->brushq1.submodels[i];
2742 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2743 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2745 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2746 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2749 mod->brushq1.firstmodelsurface = bm->firstface;
2750 mod->brushq1.nummodelsurfaces = bm->numfaces;
2752 // this gets altered below if sky is used
2753 mod->DrawSky = NULL;
2754 mod->Draw = R_Model_Brush_Draw;
2755 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2756 mod->DrawLight = R_Model_Brush_DrawLight;
2757 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2758 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2759 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2760 Mod_Q1BSP_BuildPVSTextureChains(mod);
2761 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2762 if (mod->brushq1.nummodelsurfaces)
2764 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2765 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2766 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2769 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2771 // we only need to have a drawsky function if it is used(usually only on world model)
2772 if (surf->texinfo->texture->shader == &Cshader_sky)
2773 mod->DrawSky = R_Model_Brush_DrawSky;
2774 // LordHavoc: submodels always clip, even if water
2775 if (mod->brush.numsubmodels - 1)
2776 surf->flags |= SURF_SOLIDCLIP;
2777 // calculate bounding shapes
2778 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
2780 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2781 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2782 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2783 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2784 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2785 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2786 dist = vec[0]*vec[0]+vec[1]*vec[1];
2787 if (modelyawradius < dist)
2788 modelyawradius = dist;
2789 dist += vec[2]*vec[2];
2790 if (modelradius < dist)
2794 modelyawradius = sqrt(modelyawradius);
2795 modelradius = sqrt(modelradius);
2796 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2797 mod->yawmins[2] = mod->normalmins[2];
2798 mod->yawmaxs[2] = mod->normalmaxs[2];
2799 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2800 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2801 mod->radius = modelradius;
2802 mod->radius2 = modelradius * modelradius;
2806 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2807 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2809 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2811 mod->brushq1.visleafs = bm->visleafs;
2813 // LordHavoc: only register submodels if it is the world
2814 // (prevents bsp models from replacing world submodels)
2815 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2818 // duplicate the basic information
2819 sprintf(name, "*%i", i+1);
2820 loadmodel = Mod_FindName(name);
2822 strcpy(loadmodel->name, name);
2823 // textures and memory belong to the main model
2824 loadmodel->texturepool = NULL;
2825 loadmodel->mempool = NULL;
2830 loadmodel = originalloadmodel;
2831 //Mod_Q1BSP_ProcessLightList();
2834 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2838 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2845 in = (void *)(mod_base + l->fileofs);
2846 if (l->filelen % sizeof(*in))
2847 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2848 count = l->filelen / sizeof(*in);
2849 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2852 loadmodel->num = count;
2854 for (i = 0;i < count;i++, in++, out++)
2860 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2867 in = (void *)(mod_base + l->fileofs);
2868 if (l->filelen % sizeof(*in))
2869 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2870 count = l->filelen / sizeof(*in);
2871 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2874 loadmodel->num = count;
2876 for (i = 0;i < count;i++, in++, out++)
2882 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2889 in = (void *)(mod_base + l->fileofs);
2890 if (l->filelen % sizeof(*in))
2891 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2892 count = l->filelen / sizeof(*in);
2893 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2896 loadmodel->num = count;
2898 for (i = 0;i < count;i++, in++, out++)
2904 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2911 in = (void *)(mod_base + l->fileofs);
2912 if (l->filelen % sizeof(*in))
2913 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2914 count = l->filelen / sizeof(*in);
2915 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2918 loadmodel->num = count;
2920 for (i = 0;i < count;i++, in++, out++)
2926 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2933 in = (void *)(mod_base + l->fileofs);
2934 if (l->filelen % sizeof(*in))
2935 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2936 count = l->filelen / sizeof(*in);
2937 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2940 loadmodel->num = count;
2942 for (i = 0;i < count;i++, in++, out++)
2948 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2955 in = (void *)(mod_base + l->fileofs);
2956 if (l->filelen % sizeof(*in))
2957 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2958 count = l->filelen / sizeof(*in);
2959 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2962 loadmodel->num = count;
2964 for (i = 0;i < count;i++, in++, out++)
2970 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2977 in = (void *)(mod_base + l->fileofs);
2978 if (l->filelen % sizeof(*in))
2979 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2980 count = l->filelen / sizeof(*in);
2981 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2984 loadmodel->num = count;
2986 for (i = 0;i < count;i++, in++, out++)
2992 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
2999 in = (void *)(mod_base + l->fileofs);
3000 if (l->filelen % sizeof(*in))
3001 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3002 count = l->filelen / sizeof(*in);
3003 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3006 loadmodel->num = count;
3008 for (i = 0;i < count;i++, in++, out++)
3014 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3021 in = (void *)(mod_base + l->fileofs);
3022 if (l->filelen % sizeof(*in))
3023 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3024 count = l->filelen / sizeof(*in);
3025 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3028 loadmodel->num = count;
3030 for (i = 0;i < count;i++, in++, out++)
3036 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3043 in = (void *)(mod_base + l->fileofs);
3044 if (l->filelen % sizeof(*in))
3045 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3046 count = l->filelen / sizeof(*in);
3047 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3050 loadmodel->num = count;
3052 for (i = 0;i < count;i++, in++, out++)
3058 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3065 in = (void *)(mod_base + l->fileofs);
3066 if (l->filelen % sizeof(*in))
3067 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3068 count = l->filelen / sizeof(*in);
3069 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3072 loadmodel->num = count;
3074 for (i = 0;i < count;i++, in++, out++)
3080 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3087 in = (void *)(mod_base + l->fileofs);
3088 if (l->filelen % sizeof(*in))
3089 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3090 count = l->filelen / sizeof(*in);
3091 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3094 loadmodel->num = count;
3096 for (i = 0;i < count;i++, in++, out++)
3102 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3109 in = (void *)(mod_base + l->fileofs);
3110 if (l->filelen % sizeof(*in))
3111 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3112 count = l->filelen / sizeof(*in);
3113 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3116 loadmodel->num = count;
3118 for (i = 0;i < count;i++, in++, out++)
3124 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3131 in = (void *)(mod_base + l->fileofs);
3132 if (l->filelen % sizeof(*in))
3133 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3134 count = l->filelen / sizeof(*in);
3135 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3138 loadmodel->num = count;
3140 for (i = 0;i < count;i++, in++, out++)
3146 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3153 in = (void *)(mod_base + l->fileofs);
3154 if (l->filelen % sizeof(*in))
3155 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3156 count = l->filelen / sizeof(*in);
3157 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3160 loadmodel->num = count;
3162 for (i = 0;i < count;i++, in++, out++)
3168 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3175 in = (void *)(mod_base + l->fileofs);
3176 if (l->filelen % sizeof(*in))
3177 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3178 count = l->filelen / sizeof(*in);
3179 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3182 loadmodel->num = count;
3184 for (i = 0;i < count;i++, in++, out++)
3190 static void Mod_Q2BSP_LoadModels(lump_t *l)
3197 in = (void *)(mod_base + l->fileofs);
3198 if (l->filelen % sizeof(*in))
3199 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3200 count = l->filelen / sizeof(*in);
3201 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3204 loadmodel->num = count;
3206 for (i = 0;i < count;i++, in++, out++)
3212 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3215 q2dheader_t *header;
3217 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3219 mod->type = mod_brushq2;
3221 header = (q2dheader_t *)buffer;
3223 i = LittleLong(header->version);
3224 if (i != Q2BSPVERSION)
3225 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3226 mod->brush.ishlbsp = false;
3227 if (loadmodel->isworldmodel)
3229 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3230 // until we get a texture for it...
3234 mod_base = (qbyte *)header;
3236 // swap all the lumps
3237 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3238 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3240 // store which lightmap format to use
3241 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3243 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3244 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3245 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3246 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3247 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3248 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3249 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3250 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3251 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3252 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3253 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3254 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3255 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3256 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3257 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3258 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3259 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3260 // LordHavoc: must go last because this makes the submodels
3261 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3264 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3265 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3267 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3270 char key[128], value[4096];
3272 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3273 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3274 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3277 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3278 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3279 data = loadmodel->brush.entities;
3280 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3281 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3285 if (!COM_ParseToken(&data, false))
3287 if (com_token[0] == '}')
3288 break; // end of worldspawn
3289 if (com_token[0] == '_')
3290 strcpy(key, com_token + 1);
3292 strcpy(key, com_token);
3293 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3294 key[strlen(key)-1] = 0;
3295 if (!COM_ParseToken(&data, false))
3297 strcpy(value, com_token);
3298 if (!strcmp("gridsize", key))
3300 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3301 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3307 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3317 char shadername[Q3PATHLENGTH];
3319 in = (void *)(mod_base + l->fileofs);
3320 if (l->filelen % sizeof(*in))
3321 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3322 count = l->filelen / sizeof(*in);
3323 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3325 loadmodel->brushq3.data_textures = out;
3326 loadmodel->brushq3.num_textures = count;
3328 for (i = 0;i < count;i++, in++, out++)
3331 strlcpy (out->name, in->name, sizeof (out->name));
3332 out->surfaceflags = LittleLong(in->surfaceflags);
3333 out->nativecontents = LittleLong(in->contents);
3334 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3335 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3336 out->surfaceparms = -1;
3339 // do a quick parse of shader files to get surfaceparms
3340 if ((search = FS_Search("scripts/*.shader", true, false)))
3342 for (i = 0;i < search->numfilenames;i++)
3344 if ((f = FS_LoadFile(search->filenames[i], false)))
3347 while (COM_ParseToken(&text, false))
3349 snprintf(shadername, sizeof(shadername), "%s", com_token);
3351 if (COM_ParseToken(&text, false) && !strcmp(com_token, "{"))
3353 while (COM_ParseToken(&text, false))
3355 if (!strcmp(com_token, "}"))
3357 else if (!strcmp(com_token, "{"))
3359 while (COM_ParseToken(&text, false))
3361 if (!strcmp(com_token, "}"))
3365 else if (!strcmp(com_token, "surfaceparm"))
3367 if (COM_ParseToken(&text, true) && strcmp(com_token, "\n"))
3369 if (!strcmp(com_token, "alphashadow"))
3370 flags |= Q3SURFACEPARM_ALPHASHADOW;
3371 else if (!strcmp(com_token, "areaportal"))
3372 flags |= Q3SURFACEPARM_AREAPORTAL;
3373 else if (!strcmp(com_token, "clusterportal"))
3374 flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3375 else if (!strcmp(com_token, "detail"))
3376 flags |= Q3SURFACEPARM_DETAIL;
3377 else if (!strcmp(com_token, "donotenter"))
3378 flags |= Q3SURFACEPARM_DONOTENTER;
3379 else if (!strcmp(com_token, "fog"))
3380 flags |= Q3SURFACEPARM_FOG;
3381 else if (!strcmp(com_token, "lava"))
3382 flags |= Q3SURFACEPARM_LAVA;
3383 else if (!strcmp(com_token, "lightfilter"))
3384 flags |= Q3SURFACEPARM_LIGHTFILTER;
3385 else if (!strcmp(com_token, "metalsteps"))
3386 flags |= Q3SURFACEPARM_METALSTEPS;
3387 else if (!strcmp(com_token, "nodamage"))
3388 flags |= Q3SURFACEPARM_NODAMAGE;
3389 else if (!strcmp(com_token, "nodlight"))
3390 flags |= Q3SURFACEPARM_NODLIGHT;
3391 else if (!strcmp(com_token, "nodraw"))
3392 flags |= Q3SURFACEPARM_NODRAW;
3393 else if (!strcmp(com_token, "nodrop"))
3394 flags |= Q3SURFACEPARM_NODROP;
3395 else if (!strcmp(com_token, "noimpact"))
3396 flags |= Q3SURFACEPARM_NOIMPACT;
3397 else if (!strcmp(com_token, "nolightmap"))
3398 flags |= Q3SURFACEPARM_NOLIGHTMAP;
3399 else if (!strcmp(com_token, "nomarks"))
3400 flags |= Q3SURFACEPARM_NOMARKS;
3401 else if (!strcmp(com_token, "nomipmaps"))
3402 flags |= Q3SURFACEPARM_NOMIPMAPS;
3403 else if (!strcmp(com_token, "nonsolid"))
3404 flags |= Q3SURFACEPARM_NONSOLID;
3405 else if (!strcmp(com_token, "origin"))
3406 flags |= Q3SURFACEPARM_ORIGIN;
3407 else if (!strcmp(com_token, "playerclip"))
3408 flags |= Q3SURFACEPARM_PLAYERCLIP;
3409 else if (!strcmp(com_token, "sky"))
3410 flags |= Q3SURFACEPARM_SKY;
3411 else if (!strcmp(com_token, "slick"))
3412 flags |= Q3SURFACEPARM_SLICK;
3413 else if (!strcmp(com_token, "slime"))
3414 flags |= Q3SURFACEPARM_SLIME;
3415 else if (!strcmp(com_token, "structural"))
3416 flags |= Q3SURFACEPARM_STRUCTURAL;
3417 else if (!strcmp(com_token, "trans"))
3418 flags |= Q3SURFACEPARM_TRANS;
3419 else if (!strcmp(com_token, "water"))
3420 flags |= Q3SURFACEPARM_WATER;
3422 Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
3423 if (!COM_ParseToken(&text, true) || strcmp(com_token, "\n"))
3425 Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
3431 Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
3437 // look for linebreak or }
3438 while(COM_ParseToken(&text, true) && strcmp(com_token, "\n") && strcmp(com_token, "}"));
3439 // break out to top level if it was }
3440 if (!strcmp(com_token, "}"))
3444 // add shader to list (shadername and flags)
3445 // actually here we just poke into the texture settings
3446 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3447 if (!strcmp(out->name, shadername))
3448 out->surfaceparms = flags;
3452 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3463 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3465 if (out->surfaceparms == -1)
3468 Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3469 out->surfaceparms = 0;
3470 // these are defaults
3471 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3472 || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3473 out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3474 if (!strncmp(out->name, "textures/skies/", 15))
3475 out->surfaceparms |= Q3SURFACEPARM_SKY;
3476 if (R_TextureHasAlpha(out->skin.base))
3477 out->surfaceparms |= Q3SURFACEPARM_TRANS;
3480 Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3483 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3489 in = (void *)(mod_base + l->fileofs);
3490 if (l->filelen % sizeof(*in))
3491 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3492 count = l->filelen / sizeof(*in);
3493 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3495 loadmodel->brushq3.data_planes = out;
3496 loadmodel->brushq3.num_planes = count;
3498 for (i = 0;i < count;i++, in++, out++)
3500 out->normal[0] = LittleLong(in->normal[0]);
3501 out->normal[1] = LittleLong(in->normal[1]);
3502 out->normal[2] = LittleLong(in->normal[2]);
3503 out->dist = LittleLong(in->dist);
3508 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3511 q3mbrushside_t *out;
3514 in = (void *)(mod_base + l->fileofs);
3515 if (l->filelen % sizeof(*in))
3516 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3517 count = l->filelen / sizeof(*in);
3518 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3520 loadmodel->brushq3.data_brushsides = out;
3521 loadmodel->brushq3.num_brushsides = count;
3523 for (i = 0;i < count;i++, in++, out++)
3525 n = LittleLong(in->planeindex);
3526 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3527 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3528 out->plane = loadmodel->brushq3.data_planes + n;
3529 n = LittleLong(in->textureindex);
3530 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3531 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3532 out->texture = loadmodel->brushq3.data_textures + n;
3536 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3540 int i, j, n, c, count, maxplanes;
3542 winding_t *temp1, *temp2;
3544 in = (void *)(mod_base + l->fileofs);
3545 if (l->filelen % sizeof(*in))
3546 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3547 count = l->filelen / sizeof(*in);
3548 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3550 loadmodel->brushq3.data_brushes = out;
3551 loadmodel->brushq3.num_brushes = count;
3553 temp1 = Winding_New(64);
3554 temp2 = Winding_New(64);
3559 for (i = 0;i < count;i++, in++, out++)
3561 n = LittleLong(in->firstbrushside);
3562 c = LittleLong(in->numbrushsides);
3563 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3564 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3565 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3566 out->numbrushsides = c;
3567 n = LittleLong(in->textureindex);
3568 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3569 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3570 out->texture = loadmodel->brushq3.data_textures + n;
3572 // make a list of mplane_t structs to construct a colbrush from
3573 if (maxplanes < out->numbrushsides)
3575 maxplanes = out->numbrushsides;
3578 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3580 for (j = 0;j < out->numbrushsides;j++)
3582 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3583 planes[j].dist = out->firstbrushside[j].plane->dist;
3585 // make the colbrush from the planes
3586 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
3590 Winding_Free(temp1);
3591 Winding_Free(temp2);
3594 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3600 in = (void *)(mod_base + l->fileofs);
3601 if (l->filelen % sizeof(*in))
3602 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3603 count = l->filelen / sizeof(*in);
3604 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3606 loadmodel->brushq3.data_effects = out;
3607 loadmodel->brushq3.num_effects = count;
3609 for (i = 0;i < count;i++, in++, out++)
3611 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3612 n = LittleLong(in->brushindex);
3613 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3614 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3615 out->brush = loadmodel->brushq3.data_brushes + n;
3616 out->unknown = LittleLong(in->unknown);
3620 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3625 in = (void *)(mod_base + l->fileofs);
3626 if (l->filelen % sizeof(*in))
3627 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3628 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3629 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3630 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3631 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3632 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3633 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3634 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3635 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3637 for (i = 0;i < count;i++, in++)
3639 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3640 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3641 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3642 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3643 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3644 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3645 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3646 // svector/tvector are calculated later in face loading
3647 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3648 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3649 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3650 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3651 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3652 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3653 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3654 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3655 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3656 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3657 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3658 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3659 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3663 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3669 in = (void *)(mod_base + l->fileofs);
3670 if (l->filelen % sizeof(int[3]))
3671 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3672 count = l->filelen / sizeof(*in);
3673 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3675 loadmodel->brushq3.num_triangles = count / 3;
3676 loadmodel->brushq3.data_element3i = out;
3677 loadmodel->brushq3.data_neighbor3i = out + count;
3679 for (i = 0;i < count;i++, in++, out++)
3681 *out = LittleLong(*in);
3682 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3684 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3690 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3698 in = (void *)(mod_base + l->fileofs);
3699 if (l->filelen % sizeof(*in))
3700 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3701 count = l->filelen / sizeof(*in);
3702 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3704 loadmodel->brushq3.data_lightmaps = out;
3705 loadmodel->brushq3.num_lightmaps = count;
3707 for (i = 0;i < count;i++, in++, out++)
3708 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3711 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3715 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3716 //int *originalelement3i;
3717 //int *originalneighbor3i;
3718 float *originalvertex3f;
3719 //float *originalsvector3f;
3720 //float *originaltvector3f;
3721 //float *originalnormal3f;
3722 float *originalcolor4f;
3723 float *originaltexcoordtexture2f;
3724 float *originaltexcoordlightmap2f;
3727 in = (void *)(mod_base + l->fileofs);
3728 if (l->filelen % sizeof(*in))
3729 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3730 count = l->filelen / sizeof(*in);
3731 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3733 loadmodel->brushq3.data_faces = out;
3734 loadmodel->brushq3.num_faces = count;
3736 for (i = 0;i < count;i++, in++, out++)
3738 // check face type first
3739 out->type = LittleLong(in->type);
3740 if (out->type != Q3FACETYPE_POLYGON
3741 && out->type != Q3FACETYPE_PATCH
3742 && out->type != Q3FACETYPE_MESH
3743 && out->type != Q3FACETYPE_FLARE)
3745 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3746 out->num_vertices = 0;
3747 out->num_triangles = 0;
3748 out->type = 0; // error
3752 n = LittleLong(in->textureindex);
3753 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3755 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3756 out->num_vertices = 0;
3757 out->num_triangles = 0;
3758 out->type = 0; // error
3762 out->texture = loadmodel->brushq3.data_textures + n;
3763 n = LittleLong(in->effectindex);
3764 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3766 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3772 out->effect = loadmodel->brushq3.data_effects + n;
3773 n = LittleLong(in->lightmapindex);
3774 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3776 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3780 out->lightmaptexture = NULL;
3782 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3784 out->firstvertex = LittleLong(in->firstvertex);
3785 out->num_vertices = LittleLong(in->numvertices);
3786 out->firstelement = LittleLong(in->firstelement);
3787 out->num_triangles = LittleLong(in->numelements) / 3;
3788 if (out->num_triangles * 3 != LittleLong(in->numelements))
3790 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));
3791 out->num_vertices = 0;
3792 out->num_triangles = 0;
3793 out->type = 0; // error
3796 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3798 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);
3799 out->num_vertices = 0;
3800 out->num_triangles = 0;
3801 out->type = 0; // error
3804 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3806 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);
3807 out->num_vertices = 0;
3808 out->num_triangles = 0;
3809 out->type = 0; // error
3814 case Q3FACETYPE_POLYGON:
3815 case Q3FACETYPE_MESH:
3816 // no processing necessary
3817 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3818 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3819 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3820 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3821 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3822 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3823 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3824 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3825 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3827 case Q3FACETYPE_PATCH:
3828 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3829 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3830 if (patchsize[0] < 1 || patchsize[1] < 1)
3832 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3833 out->num_vertices = 0;
3834 out->num_triangles = 0;
3835 out->type = 0; // error
3838 // convert patch to Q3FACETYPE_MESH
3839 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3840 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3841 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3842 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3843 finalvertices = finalwidth * finalheight;
3844 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3845 originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3846 //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3847 //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3848 //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3849 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3850 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3851 originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3852 //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
3853 //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3855 originalvertex3f = out->data_vertex3f;
3856 //originalsvector3f = out->data_svector3f;
3857 //originaltvector3f = out->data_tvector3f;
3858 //originalnormal3f = out->data_normal3f;
3859 originalcolor4f = out->data_color4f;
3860 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3861 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3862 //originalelement3i = out->data_element3i;
3863 //originalneighbor3i = out->data_neighbor3i;
3865 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
3866 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3867 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3868 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3869 out->data_color4f = out->data_normal3f + finalvertices * 3;
3870 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3871 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3872 out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
3873 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3874 out->type = Q3FACETYPE_MESH;
3875 out->firstvertex = -1;
3876 out->num_vertices = finalvertices;
3877 out->firstelement = -1;
3878 out->num_triangles = finaltriangles;
3879 // generate geometry
3880 // (note: normals are skipped because they get recalculated)
3881 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3882 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3883 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3884 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3885 // generate elements
3886 e = out->data_element3i;
3887 for (y = 0;y < finalheight - 1;y++)
3889 row0 = (y + 0) * finalwidth;
3890 row1 = (y + 1) * finalwidth;
3891 for (x = 0;x < finalwidth - 1;x++)
3903 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3904 if (developer.integer)
3906 if (out->num_triangles < finaltriangles)
3907 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);
3909 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);
3911 // q3map does not put in collision brushes for curves... ugh
3912 out->collisions = true;
3914 case Q3FACETYPE_FLARE:
3915 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3917 out->num_vertices = 0;
3918 out->num_triangles = 0;
3922 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
3923 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3925 if (invalidelements)
3927 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);
3928 for (j = 0;j < out->num_triangles * 3;j++)
3930 Con_Printf(" %i", out->data_element3i[j]);
3931 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3932 out->data_element3i[j] = 0;
3936 // for shadow volumes
3937 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
3938 // for per pixel lighting
3939 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);
3940 // calculate a bounding box
3941 VectorClear(out->mins);
3942 VectorClear(out->maxs);
3943 if (out->num_vertices)
3945 VectorCopy(out->data_vertex3f, out->mins);
3946 VectorCopy(out->data_vertex3f, out->maxs);
3947 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
3949 out->mins[0] = min(out->mins[0], v[0]);
3950 out->maxs[0] = max(out->maxs[0], v[0]);
3951 out->mins[1] = min(out->mins[1], v[1]);
3952 out->maxs[1] = max(out->maxs[1], v[1]);
3953 out->mins[2] = min(out->mins[2], v[2]);
3954 out->maxs[2] = max(out->maxs[2], v[2]);
3956 out->mins[0] -= 1.0f;
3957 out->mins[1] -= 1.0f;
3958 out->mins[2] -= 1.0f;
3959 out->maxs[0] += 1.0f;
3960 out->maxs[1] += 1.0f;
3961 out->maxs[2] += 1.0f;
3965 // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
3968 int totalverts, totaltris;
3969 int originalnum_vertices;
3970 float *originaldata_vertex3f;
3971 float *originaldata_texcoordtexture2f;
3972 float *originaldata_texcoordlightmap2f;
3973 float *originaldata_svector3f;
3974 float *originaldata_tvector3f;
3975 float *originaldata_normal3f;
3976 float *originaldata_color4f;
3977 int originalnum_triangles;
3978 int *originaldata_element3i;
3979 int *originaldata_neighbor3i;
3983 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
3987 totalverts += out->num_vertices;
3988 totaltris += out->num_triangles;
3991 originalnum_vertices = loadmodel->brushq3.num_vertices;
3992 originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
3993 originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
3994 originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
3995 originaldata_svector3f = loadmodel->brushq3.data_svector3f;
3996 originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
3997 originaldata_normal3f = loadmodel->brushq3.data_normal3f;
3998 originaldata_color4f = loadmodel->brushq3.data_color4f;
3999 originalnum_triangles = loadmodel->brushq3.num_triangles;
4000 originaldata_element3i = loadmodel->brushq3.data_element3i;
4001 originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
4002 loadmodel->brushq3.num_vertices = totalverts;
4003 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
4004 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
4005 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
4006 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
4007 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
4008 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
4009 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
4010 loadmodel->brushq3.num_triangles = totaltris;
4011 loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
4012 loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
4015 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4019 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
4020 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
4021 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
4022 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
4023 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
4024 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
4025 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
4026 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
4027 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
4028 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
4029 if (out->firstvertex == -1)
4030 Mem_Free(out->data_vertex3f);
4031 if (out->firstelement == -1)
4032 Mem_Free(out->data_element3i);
4033 out->firstvertex = totalverts;
4034 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4035 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4036 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4037 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4038 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4039 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4040 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4041 out->firstelement = totaltris * 3;
4042 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4043 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4044 //for (j = 0;j < out->numtriangles * 3;j++)
4045 // out->data_element3i[j] += totalverts - out->firstvertex;
4046 totalverts += out->num_vertices;
4047 totaltris += out->num_triangles;
4049 Mem_Free(originaldata_vertex3f);
4050 Mem_Free(originaldata_element3i);
4055 static void Mod_Q3BSP_LoadModels(lump_t *l)
4059 int i, j, n, c, count;
4061 in = (void *)(mod_base + l->fileofs);
4062 if (l->filelen % sizeof(*in))
4063 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4064 count = l->filelen / sizeof(*in);
4065 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4067 loadmodel->brushq3.data_models = out;
4068 loadmodel->brushq3.num_models = count;
4070 for (i = 0;i < count;i++, in++, out++)
4072 for (j = 0;j < 3;j++)
4074 out->mins[j] = LittleFloat(in->mins[j]);
4075 out->maxs[j] = LittleFloat(in->maxs[j]);
4077 n = LittleLong(in->firstface);
4078 c = LittleLong(in->numfaces);
4079 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4080 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4081 out->firstface = loadmodel->brushq3.data_faces + n;
4083 n = LittleLong(in->firstbrush);
4084 c = LittleLong(in->numbrushes);
4085 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4086 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4087 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4088 out->numbrushes = c;
4092 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4098 in = (void *)(mod_base + l->fileofs);
4099 if (l->filelen % sizeof(*in))
4100 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4101 count = l->filelen / sizeof(*in);
4102 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4104 loadmodel->brushq3.data_leafbrushes = out;
4105 loadmodel->brushq3.num_leafbrushes = count;
4107 for (i = 0;i < count;i++, in++, out++)
4109 n = LittleLong(*in);
4110 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4111 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4112 *out = loadmodel->brushq3.data_brushes + n;
4116 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4122 in = (void *)(mod_base + l->fileofs);
4123 if (l->filelen % sizeof(*in))
4124 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4125 count = l->filelen / sizeof(*in);
4126 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4128 loadmodel->brushq3.data_leaffaces = out;
4129 loadmodel->brushq3.num_leaffaces = count;
4131 for (i = 0;i < count;i++, in++, out++)
4133 n = LittleLong(*in);
4134 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4135 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4136 *out = loadmodel->brushq3.data_faces + n;
4140 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4144 int i, j, n, c, count;
4146 in = (void *)(mod_base + l->fileofs);
4147 if (l->filelen % sizeof(*in))
4148 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4149 count = l->filelen / sizeof(*in);
4150 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4152 loadmodel->brushq3.data_leafs = out;
4153 loadmodel->brushq3.num_leafs = count;
4155 for (i = 0;i < count;i++, in++, out++)
4157 out->isnode = false;
4159 out->clusterindex = LittleLong(in->clusterindex);
4160 out->areaindex = LittleLong(in->areaindex);
4161 for (j = 0;j < 3;j++)
4163 // yes the mins/maxs are ints
4164 out->mins[j] = LittleLong(in->mins[j]);
4165 out->maxs[j] = LittleLong(in->maxs[j]);
4167 n = LittleLong(in->firstleafface);
4168 c = LittleLong(in->numleaffaces);
4169 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4170 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4171 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4172 out->numleaffaces = c;
4173 n = LittleLong(in->firstleafbrush);
4174 c = LittleLong(in->numleafbrushes);
4175 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4176 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4177 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4178 out->numleafbrushes = c;
4182 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4185 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4186 node->parent = parent;
4189 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4190 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4194 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4200 in = (void *)(mod_base + l->fileofs);
4201 if (l->filelen % sizeof(*in))
4202 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4203 count = l->filelen / sizeof(*in);
4204 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4206 loadmodel->brushq3.data_nodes = out;
4207 loadmodel->brushq3.num_nodes = count;
4209 for (i = 0;i < count;i++, in++, out++)
4213 n = LittleLong(in->planeindex);
4214 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4215 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4216 out->plane = loadmodel->brushq3.data_planes + n;
4217 for (j = 0;j < 2;j++)
4219 n = LittleLong(in->childrenindex[j]);
4222 if (n >= loadmodel->brushq3.num_nodes)
4223 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4224 out->children[j] = loadmodel->brushq3.data_nodes + n;
4229 if (n >= loadmodel->brushq3.num_leafs)
4230 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4231 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4234 for (j = 0;j < 3;j++)
4236 // yes the mins/maxs are ints
4237 out->mins[j] = LittleLong(in->mins[j]);
4238 out->maxs[j] = LittleLong(in->maxs[j]);
4242 // set the parent pointers
4243 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4246 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4249 q3dlightgrid_t *out;
4252 if (l->filelen == 0)
4255 in = (void *)(mod_base + l->fileofs);
4256 if (l->filelen % sizeof(*in))
4257 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4258 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4259 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4260 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4261 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4262 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4263 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4264 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4265 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4266 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4267 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4268 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4269 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4270 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4271 if (l->filelen < count * (int)sizeof(*in))
4272 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]);
4273 if (l->filelen != count * (int)sizeof(*in))
4274 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4276 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4277 loadmodel->brushq3.data_lightgrid = out;
4278 loadmodel->brushq3.num_lightgrid = count;
4280 // no swapping or validation necessary
4281 memcpy(out, in, count * (int)sizeof(*out));
4283 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]);
4284 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]);
4287 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4292 if (l->filelen == 0)
4295 in = (void *)(mod_base + l->fileofs);
4297 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4299 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4300 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4301 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4302 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4303 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4304 if (l->filelen < totalchains + (int)sizeof(*in))
4305 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);
4307 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4308 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4311 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4313 // FIXME: finish this code
4314 VectorCopy(in, out);
4317 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4319 int i, j, k, index[3];
4320 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4321 q3dlightgrid_t *a, *s;
4322 // FIXME: write this
4323 if (!model->brushq3.num_lightgrid)
4325 ambientcolor[0] = 1;
4326 ambientcolor[1] = 1;
4327 ambientcolor[2] = 1;
4330 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4331 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4332 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4333 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4334 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4335 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4336 index[0] = (int)floor(transformed[0]);
4337 index[1] = (int)floor(transformed[1]);
4338 index[2] = (int)floor(transformed[2]);
4339 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4340 // now lerp the values
4341 VectorClear(diffusenormal);
4342 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4343 for (k = 0;k < 2;k++)
4345 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4346 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4348 for (j = 0;j < 2;j++)
4350 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4351 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4353 for (i = 0;i < 2;i++)
4355 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4356 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4358 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4359 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4360 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4361 pitch = s->diffusepitch * M_PI / 128;
4362 yaw = s->diffuseyaw * M_PI / 128;
4363 sinpitch = sin(pitch);
4364 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4365 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4366 diffusenormal[2] += blend * (cos(pitch));
4367 //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)));
4371 VectorNormalize(diffusenormal);
4372 //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]);
4375 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)
4377 int i, startside, endside;
4378 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4382 if (startfrac >= trace->fraction)
4384 // note: all line fragments past first impact fraction are ignored
4385 while (node->isnode)
4387 // recurse down node sides
4388 dist1 = PlaneDiff(start, node->plane);
4389 dist2 = PlaneDiff(end, node->plane);
4390 startside = dist1 < 0;
4391 endside = dist2 < 0;
4392 if (startside == endside)
4394 // most of the time the line fragment is on one side of the plane
4395 node = node->children[startside];
4399 // line crosses node plane, split the line
4400 midfrac = dist1 / (dist1 - dist2);
4401 VectorLerp(linestart, midfrac, lineend, mid);
4402 // take the near side first
4403 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4404 if (midfrac < trace->fraction)
4405 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4410 segmentmins[0] = min(start[0], end[0]);
4411 segmentmins[1] = min(start[1], end[1]);
4412 segmentmins[2] = min(start[2], end[2]);
4413 segmentmaxs[0] = max(start[0], end[0]);
4414 segmentmaxs[1] = max(start[1], end[1]);
4415 segmentmaxs[2] = max(start[2], end[2]);
4416 leaf = (q3mleaf_t *)node;
4417 for (i = 0;i < leaf->numleafbrushes;i++)
4419 if (startfrac >= trace->fraction)
4421 brush = leaf->firstleafbrush[i]->colbrushf;
4422 if (brush && brush->markframe != markframe)
4424 brush->markframe = markframe;
4425 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4426 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4429 if (mod_q3bsp_curves_collisions.integer)
4431 for (i = 0;i < leaf->numleaffaces;i++)
4433 if (startfrac >= trace->fraction)
4435 face = leaf->firstleafface[i];
4436 if (face->collisions && face->collisionmarkframe != markframe)
4438 face->collisionmarkframe = markframe;
4439 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4440 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4446 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)
4449 float nodesegmentmins[3], nodesegmentmaxs[3];
4453 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4454 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4455 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4456 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4457 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4458 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4459 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4463 // recurse down node sides
4464 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4467 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4468 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4470 else if (sides == 2)
4471 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4473 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4475 dist = node->plane->dist - (1.0f / 8.0f);
4476 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4478 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4480 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4486 dist = node->plane->dist + (1.0f / 8.0f);
4487 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4489 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4491 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4497 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4499 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4501 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4507 leaf = (q3mleaf_t *)node;
4508 for (i = 0;i < leaf->numleafbrushes;i++)
4510 brush = leaf->firstleafbrush[i]->colbrushf;
4511 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4513 brush->markframe = markframe;
4514 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4517 if (mod_q3bsp_curves_collisions.integer)
4519 for (i = 0;i < leaf->numleaffaces;i++)
4521 face = leaf->firstleafface[i];
4522 // 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
4523 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4524 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4530 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)
4533 float segmentmins[3], segmentmaxs[3];
4534 colbrushf_t *thisbrush_start, *thisbrush_end;
4535 matrix4x4_t startmatrix, endmatrix;
4536 static int markframe = 0;
4538 memset(trace, 0, sizeof(*trace));
4539 trace->fraction = 1;
4540 trace->hitsupercontentsmask = hitsupercontentsmask;
4541 Matrix4x4_CreateIdentity(&startmatrix);
4542 Matrix4x4_CreateIdentity(&endmatrix);
4543 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4544 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4545 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4546 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4547 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4548 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4549 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4552 if (model->brushq3.submodel)
4554 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4555 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4556 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4557 if (mod_q3bsp_curves_collisions.integer)
4559 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4561 face = model->brushq3.data_thismodel->firstface + i;
4562 if (face->collisions)
4563 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4568 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4572 // box trace, performed as brush trace
4573 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4574 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4575 if (model->brushq3.submodel)
4577 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4578 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4579 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4580 if (mod_q3bsp_curves_collisions.integer)
4582 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4584 face = model->brushq3.data_thismodel->firstface + i;
4585 if (face->collisions)
4586 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4591 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4596 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)
4603 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4604 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4607 // node - recurse down the BSP tree
4608 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4611 node = node->children[0];
4614 node = node->children[1];
4616 default: // crossing
4617 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4619 node = node->children[1];
4626 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4628 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4631 //Returns PVS data for a given point
4632 //(note: can return NULL)
4633 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4636 Mod_CheckLoaded(model);
4637 node = model->brushq3.data_nodes;
4638 while (node->isnode)
4639 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4640 if (((q3mleaf_t *)node)->clusterindex >= 0)
4641 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4646 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4652 while (node->isnode)
4654 d = PlaneDiff(org, node->plane);
4656 node = node->children[0];
4657 else if (d < -radius)
4658 node = node->children[1];
4661 // go down both sides
4662 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4663 node = node->children[1];
4666 // if this is a leaf with a pvs, accumulate the pvs bits
4667 if (((q3mleaf_t *)node)->clusterindex >= 0)
4669 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4670 for (i = 0;i < pvsbytes;i++)
4671 pvsbuffer[i] |= pvs[i];
4676 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4677 //of the given point.
4678 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4680 int bytes = model->brushq3.num_pvschainlength;
4681 bytes = min(bytes, pvsbufferlength);
4682 memset(pvsbuffer, 0, bytes);
4683 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4688 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4690 int supercontents = 0;
4691 if (nativecontents & Q2CONTENTS_SOLID)
4692 supercontents |= SUPERCONTENTS_SOLID;
4693 if (nativecontents & Q2CONTENTS_WATER)
4694 supercontents |= SUPERCONTENTS_WATER;
4695 if (nativecontents & Q2CONTENTS_SLIME)
4696 supercontents |= SUPERCONTENTS_SLIME;
4697 if (nativecontents & Q2CONTENTS_LAVA)
4698 supercontents |= SUPERCONTENTS_LAVA;
4699 return supercontents;
4702 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4704 int nativecontents = 0;
4705 if (supercontents & SUPERCONTENTS_SOLID)
4706 nativecontents |= Q2CONTENTS_SOLID;
4707 if (supercontents & SUPERCONTENTS_WATER)
4708 nativecontents |= Q2CONTENTS_WATER;
4709 if (supercontents & SUPERCONTENTS_SLIME)
4710 nativecontents |= Q2CONTENTS_SLIME;
4711 if (supercontents & SUPERCONTENTS_LAVA)
4712 nativecontents |= Q2CONTENTS_LAVA;
4713 return nativecontents;
4716 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4717 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4718 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4719 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);
4720 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4723 q3dheader_t *header;
4724 float corner[3], yawradius, modelradius;
4726 mod->type = mod_brushq3;
4730 header = (q3dheader_t *)buffer;
4732 i = LittleLong(header->version);
4733 if (i != Q3BSPVERSION)
4734 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4735 if (loadmodel->isworldmodel)
4737 Cvar_SetValue("halflifebsp", false);
4738 // until we get a texture for it...
4742 mod->soundfromcenter = true;
4743 mod->TraceBox = Mod_Q3BSP_TraceBox;
4744 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4745 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4746 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4747 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4748 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4749 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4750 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4751 //mod->DrawSky = R_Q3BSP_DrawSky;
4752 mod->Draw = R_Q3BSP_Draw;
4753 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4754 mod->DrawLight = R_Q3BSP_DrawLight;
4756 mod_base = (qbyte *)header;
4758 // swap all the lumps
4759 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4760 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4762 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4763 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4764 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4765 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4766 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4767 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4768 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4769 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4770 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4771 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4772 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4773 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4774 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4775 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4776 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4777 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4778 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4779 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4781 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4788 // LordHavoc: only register submodels if it is the world
4789 // (prevents bsp models from replacing world submodels)
4790 if (!loadmodel->isworldmodel)
4792 // duplicate the basic information
4793 sprintf(name, "*%i", i);
4794 mod = Mod_FindName(name);
4796 strcpy(mod->name, name);
4797 // textures and memory belong to the main model
4798 mod->texturepool = NULL;
4799 mod->mempool = NULL;
4801 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4802 mod->brushq3.submodel = i;
4804 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4805 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4806 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4807 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4808 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4809 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4810 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4811 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4812 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4813 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4814 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4815 mod->yawmins[2] = mod->normalmins[2];
4816 mod->yawmaxs[2] = mod->normalmaxs[2];
4817 mod->radius = modelradius;
4818 mod->radius2 = modelradius * modelradius;
4820 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
4821 if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
4823 if (j < mod->brushq3.data_thismodel->numfaces)
4824 mod->DrawSky = R_Q3BSP_DrawSky;
4828 void Mod_IBSP_Load(model_t *mod, void *buffer)
4830 int i = LittleLong(((int *)buffer)[1]);
4831 if (i == Q3BSPVERSION)
4832 Mod_Q3BSP_Load(mod,buffer);
4833 else if (i == Q2BSPVERSION)
4834 Mod_Q2BSP_Load(mod,buffer);
4836 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4839 void Mod_MAP_Load(model_t *mod, void *buffer)
4841 Host_Error("Mod_MAP_Load: not yet implemented\n");