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.
25 // note: model_shared.c sets up r_notexture, and r_surf_notexture
27 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
29 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
30 cvar_t halflifebsp = {0, "halflifebsp", "0"};
31 cvar_t r_novis = {0, "r_novis", "0"};
32 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
33 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
34 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
35 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
37 void Mod_BrushInit(void)
39 // Cvar_RegisterVariable(&r_subdivide_size);
40 Cvar_RegisterVariable(&halflifebsp);
41 Cvar_RegisterVariable(&r_novis);
42 Cvar_RegisterVariable(&r_miplightmaps);
43 Cvar_RegisterVariable(&r_lightmaprgba);
44 Cvar_RegisterVariable(&r_nosurftextures);
45 Cvar_RegisterVariable(&r_sortsurfaces);
46 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
49 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
56 Mod_CheckLoaded(model);
58 // LordHavoc: modified to start at first clip node,
59 // in other words: first node of the (sub)model
60 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
61 while (node->contents == 0)
62 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
64 return (mleaf_t *)node;
68 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)
72 if (node->contents < 0)
75 if (node->contents == CONTENTS_SOLID)
77 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
78 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
81 // node - recurse down the BSP tree
82 switch (BoxOnPlaneSide(mins, maxs, node->plane))
85 node = node->children[0];
88 node = node->children[1];
91 if (node->children[0]->contents != CONTENTS_SOLID)
92 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
94 node = node->children[1];
101 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
103 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
107 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
112 return CONTENTS_EMPTY;
114 Mod_CheckLoaded(model);
116 // LordHavoc: modified to start at first clip node,
117 // in other words: first node of the (sub)model
118 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
119 while (node->contents == 0)
120 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
122 return ((mleaf_t *)node)->contents;
126 typedef struct findnonsolidlocationinfo_s
134 findnonsolidlocationinfo_t;
137 extern cvar_t samelevel;
139 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
141 int i, surfnum, k, *tri, *mark;
142 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
148 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
150 surf = info->model->brushq1.surfaces + *mark;
151 if (surf->flags & SURF_SOLIDCLIP)
154 VectorCopy(surf->plane->normal, surfnormal);
155 if (surf->flags & SURF_PLANEBACK)
156 VectorNegate(surfnormal, surfnormal);
158 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
160 for (k = 0;k < mesh->numtriangles;k++)
162 tri = mesh->element3i + k * 3;
163 VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
164 VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
165 VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
166 VectorSubtract(vert[1], vert[0], edge[0]);
167 VectorSubtract(vert[2], vert[1], edge[1]);
168 CrossProduct(edge[1], edge[0], facenormal);
169 if (facenormal[0] || facenormal[1] || facenormal[2])
171 VectorNormalize(facenormal);
173 if (VectorDistance(facenormal, surfnormal) > 0.01f)
174 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
176 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
177 if (f <= info->bestdist && f >= -info->bestdist)
179 VectorSubtract(vert[0], vert[2], edge[2]);
180 VectorNormalize(edge[0]);
181 VectorNormalize(edge[1]);
182 VectorNormalize(edge[2]);
183 CrossProduct(facenormal, edge[0], edgenormal[0]);
184 CrossProduct(facenormal, edge[1], edgenormal[1]);
185 CrossProduct(facenormal, edge[2], edgenormal[2]);
187 if (samelevel.integer & 1)
188 VectorNegate(edgenormal[0], edgenormal[0]);
189 if (samelevel.integer & 2)
190 VectorNegate(edgenormal[1], edgenormal[1]);
191 if (samelevel.integer & 4)
192 VectorNegate(edgenormal[2], edgenormal[2]);
193 for (i = 0;i < 3;i++)
194 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
195 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
196 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
197 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]);
200 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
201 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
202 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
204 // we got lucky, the center is within the face
205 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
209 if (info->bestdist > dist)
211 info->bestdist = dist;
212 VectorScale(facenormal, (info->radius - -dist), info->nudge);
217 if (info->bestdist > dist)
219 info->bestdist = dist;
220 VectorScale(facenormal, (info->radius - dist), info->nudge);
226 // check which edge or vertex the center is nearest
227 for (i = 0;i < 3;i++)
229 f = DotProduct(info->center, edge[i]);
230 if (f >= DotProduct(vert[0], edge[i])
231 && f <= DotProduct(vert[1], edge[i]))
234 VectorMA(info->center, -f, edge[i], point);
235 dist = sqrt(DotProduct(point, point));
236 if (info->bestdist > dist)
238 info->bestdist = dist;
239 VectorScale(point, (info->radius / dist), info->nudge);
241 // skip both vertex checks
242 // (both are further away than this edge)
247 // not on edge, check first vertex of edge
248 VectorSubtract(info->center, vert[i], point);
249 dist = sqrt(DotProduct(point, point));
250 if (info->bestdist > dist)
252 info->bestdist = dist;
253 VectorScale(point, (info->radius / dist), info->nudge);
266 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
270 if (((mleaf_t *)node)->nummarksurfaces)
271 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
275 float f = PlaneDiff(info->center, node->plane);
276 if (f >= -info->bestdist)
277 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
278 if (f <= info->bestdist)
279 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
283 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
286 findnonsolidlocationinfo_t info;
292 VectorCopy(in, info.center);
293 info.radius = radius;
298 VectorClear(info.nudge);
299 info.bestdist = radius;
300 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
301 VectorAdd(info.center, info.nudge, info.center);
303 while (info.bestdist < radius && ++i < 10);
304 VectorCopy(info.center, out);
309 // the hull we're tracing through
312 // the trace structure to fill in
315 // start, end, and end - start (in model space)
320 RecursiveHullCheckTraceInfo_t;
322 // 1/32 epsilon to keep floating point happy
323 #define DIST_EPSILON (0.03125)
325 #define HULLCHECKSTATE_EMPTY 0
326 #define HULLCHECKSTATE_SOLID 1
327 #define HULLCHECKSTATE_DONE 2
329 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
331 // status variables, these don't need to be saved on the stack when
332 // recursing... but are because this should be thread-safe
333 // (note: tracing against a bbox is not thread-safe, yet)
338 // variables that need to be stored on the stack when recursing
343 // LordHavoc: a goto! everyone flee in terror... :)
348 t->trace->endcontents = num;
349 if (t->trace->thiscontents)
351 if (num == t->trace->thiscontents)
352 t->trace->allsolid = false;
355 // if the first leaf is solid, set startsolid
356 if (t->trace->allsolid)
357 t->trace->startsolid = true;
358 return HULLCHECKSTATE_SOLID;
360 return HULLCHECKSTATE_EMPTY;
364 if (num != CONTENTS_SOLID)
366 t->trace->allsolid = false;
367 if (num == CONTENTS_EMPTY)
368 t->trace->inopen = true;
370 t->trace->inwater = true;
374 // if the first leaf is solid, set startsolid
375 if (t->trace->allsolid)
376 t->trace->startsolid = true;
377 return HULLCHECKSTATE_SOLID;
379 return HULLCHECKSTATE_EMPTY;
383 // find the point distances
384 node = t->hull->clipnodes + num;
386 plane = t->hull->planes + node->planenum;
389 t1 = p1[plane->type] - plane->dist;
390 t2 = p2[plane->type] - plane->dist;
394 t1 = DotProduct (plane->normal, p1) - plane->dist;
395 t2 = DotProduct (plane->normal, p2) - plane->dist;
402 num = node->children[1];
411 num = node->children[0];
417 // the line intersects, find intersection point
418 // LordHavoc: this uses the original trace for maximum accuracy
421 t1 = t->start[plane->type] - plane->dist;
422 t2 = t->end[plane->type] - plane->dist;
426 t1 = DotProduct (plane->normal, t->start) - plane->dist;
427 t2 = DotProduct (plane->normal, t->end) - plane->dist;
430 midf = t1 / (t1 - t2);
431 midf = bound(p1f, midf, p2f);
432 VectorMA(t->start, midf, t->dist, mid);
434 // recurse both sides, front side first
435 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
436 // if this side is not empty, return what it is (solid or done)
437 if (ret != HULLCHECKSTATE_EMPTY)
440 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
441 // if other side is not solid, return what it is (empty or done)
442 if (ret != HULLCHECKSTATE_SOLID)
445 // front is air and back is solid, this is the impact point...
448 t->trace->plane.dist = -plane->dist;
449 VectorNegate (plane->normal, t->trace->plane.normal);
453 t->trace->plane.dist = plane->dist;
454 VectorCopy (plane->normal, t->trace->plane.normal);
457 // bias away from surface a bit
458 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
459 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
461 midf = t1 / (t1 - t2);
462 t->trace->fraction = bound(0.0f, midf, 1.0);
464 return HULLCHECKSTATE_DONE;
467 static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs)
469 // this function currently only supports same size start and end
471 RecursiveHullCheckTraceInfo_t rhc;
473 memset(&rhc, 0, sizeof(rhc));
474 memset(trace, 0, sizeof(trace_t));
476 rhc.trace->fraction = 1;
477 rhc.trace->allsolid = true;
478 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
480 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
481 else if (model->brushq1.ishlbsp)
483 if (boxsize[0] <= 32)
485 if (boxsize[2] < 54) // pick the nearest of 36 or 72
486 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
488 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
491 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
495 if (boxsize[0] <= 32)
496 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
498 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
500 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
501 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
502 VectorSubtract(rhc.end, rhc.start, rhc.dist);
503 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
506 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)
508 int side, distz = endz - startz;
513 if (node->contents < 0)
514 return false; // didn't hit anything
516 switch (node->plane->type)
519 node = node->children[x < node->plane->dist];
522 node = node->children[y < node->plane->dist];
525 side = startz < node->plane->dist;
526 if ((endz < node->plane->dist) == side)
528 node = node->children[side];
531 // found an intersection
532 mid = node->plane->dist;
535 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
536 front += startz * node->plane->normal[2];
537 back += endz * node->plane->normal[2];
538 side = front < node->plane->dist;
539 if ((back < node->plane->dist) == side)
541 node = node->children[side];
544 // found an intersection
545 mid = startz + distz * (front - node->plane->dist) / (front - back);
549 // go down front side
550 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
551 return true; // hit something
554 // check for impact on this node
555 if (node->numsurfaces)
560 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
561 for (i = 0;i < node->numsurfaces;i++, surf++)
563 if (!(surf->flags & SURF_LIGHTMAP))
564 continue; // no lightmaps
566 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]);
567 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]);
569 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
572 ds -= surf->texturemins[0];
573 dt -= surf->texturemins[1];
575 if (ds > surf->extents[0] || dt > surf->extents[1])
581 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;
582 line3 = ((surf->extents[0]>>4)+1)*3;
583 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
585 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
587 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
589 scale = d_lightstylevalue[surf->styles[maps]];
590 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
591 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
592 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
593 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
598 LordHavoc: here's the readable version of the interpolation
599 code, not quite as easy for the compiler to optimize...
601 dsfrac is the X position in the lightmap pixel, * 16
602 dtfrac is the Y position in the lightmap pixel, * 16
603 r00 is top left corner, r01 is top right corner
604 r10 is bottom left corner, r11 is bottom right corner
605 g and b are the same layout.
606 r0 and r1 are the top and bottom intermediate results
608 first we interpolate the top two points, to get the top
611 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
612 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
613 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
615 then we interpolate the bottom two points, to get the
618 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
619 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
620 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
622 then we interpolate the top and bottom samples to get the
623 middle sample (the one which was requested)
625 r = (((r1-r0) * dtfrac) >> 4) + r0;
626 g = (((g1-g0) * dtfrac) >> 4) + g0;
627 b = (((b1-b0) * dtfrac) >> 4) + b0;
630 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
631 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
632 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
634 return true; // success
639 node = node->children[side ^ 1];
641 distz = endz - startz;
646 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
648 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);
651 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
658 Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
666 for (c = *in++;c > 0;c--)
670 Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
679 static void Mod_Q1BSP_LoadTextures(lump_t *l)
681 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
683 texture_t *tx, *tx2, *anims[10], *altanims[10];
685 qbyte *data, *mtdata;
688 loadmodel->brushq1.textures = NULL;
693 m = (dmiptexlump_t *)(mod_base + l->fileofs);
695 m->nummiptex = LittleLong (m->nummiptex);
697 // add two slots for notexture walls and notexture liquids
698 loadmodel->brushq1.numtextures = m->nummiptex + 2;
699 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
701 // fill out all slots with notexture
702 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
705 strcpy(tx->name, "NO TEXTURE FOUND");
708 tx->skin.base = r_notexture;
709 tx->shader = &Cshader_wall_lightmap;
710 tx->flags = SURF_SOLIDCLIP;
711 if (i == loadmodel->brushq1.numtextures - 1)
713 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
714 tx->shader = &Cshader_water;
716 tx->currentframe = tx;
719 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
721 // LordHavoc: mostly rewritten map texture loader
722 for (i = 0;i < m->nummiptex;i++)
724 dofs[i] = LittleLong(dofs[i]);
725 if (dofs[i] == -1 || r_nosurftextures.integer)
727 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
729 // make sure name is no more than 15 characters
730 for (j = 0;dmiptex->name[j] && j < 15;j++)
731 name[j] = dmiptex->name[j];
734 mtwidth = LittleLong(dmiptex->width);
735 mtheight = LittleLong(dmiptex->height);
737 j = LittleLong(dmiptex->offsets[0]);
741 if (j < 40 || j + mtwidth * mtheight > l->filelen)
743 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
746 mtdata = (qbyte *)dmiptex + j;
749 if ((mtwidth & 15) || (mtheight & 15))
750 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
752 // LordHavoc: force all names to lowercase
753 for (j = 0;name[j];j++)
754 if (name[j] >= 'A' && name[j] <= 'Z')
755 name[j] += 'a' - 'A';
757 tx = loadmodel->brushq1.textures + i;
758 strcpy(tx->name, name);
760 tx->height = mtheight;
764 sprintf(tx->name, "unnamed%i", i);
765 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
768 // LordHavoc: HL sky textures are entirely different than quake
769 if (!loadmodel->brushq1.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
771 if (loadmodel->isworldmodel)
773 data = loadimagepixels(tx->name, false, 0, 0);
776 if (image_width == 256 && image_height == 128)
784 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
786 R_InitSky(mtdata, 1);
789 else if (mtdata != NULL)
790 R_InitSky(mtdata, 1);
795 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
797 // did not find external texture, load it from the bsp or wad3
798 if (loadmodel->brushq1.ishlbsp)
800 // internal texture overrides wad
801 qbyte *pixels, *freepixels, *fogpixels;
802 pixels = freepixels = NULL;
804 pixels = W_ConvertWAD3Texture(dmiptex);
806 pixels = freepixels = W_GetTexture(tx->name);
809 tx->width = image_width;
810 tx->height = image_height;
811 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);
812 if (Image_CheckAlpha(pixels, image_width * image_height, true))
814 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
815 for (j = 0;j < image_width * image_height * 4;j += 4)
817 fogpixels[j + 0] = 255;
818 fogpixels[j + 1] = 255;
819 fogpixels[j + 2] = 255;
820 fogpixels[j + 3] = pixels[j + 3];
822 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
827 Mem_Free(freepixels);
829 else if (mtdata) // texture included
830 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
833 if (tx->skin.base == NULL)
838 tx->skin.base = r_notexture;
841 if (tx->name[0] == '*')
843 // turb does not block movement
844 tx->flags &= ~SURF_SOLIDCLIP;
845 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
846 // LordHavoc: some turbulent textures should be fullbright and solid
847 if (!strncmp(tx->name,"*lava",5)
848 || !strncmp(tx->name,"*teleport",9)
849 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
850 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
852 tx->flags |= SURF_WATERALPHA;
853 tx->shader = &Cshader_water;
855 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
857 tx->flags |= SURF_DRAWSKY;
858 tx->shader = &Cshader_sky;
862 tx->flags |= SURF_LIGHTMAP;
864 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
865 tx->shader = &Cshader_wall_lightmap;
868 // start out with no animation
869 tx->currentframe = tx;
872 // sequence the animations
873 for (i = 0;i < m->nummiptex;i++)
875 tx = loadmodel->brushq1.textures + i;
876 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
878 if (tx->anim_total[0] || tx->anim_total[1])
879 continue; // already sequenced
881 // find the number of frames in the animation
882 memset(anims, 0, sizeof(anims));
883 memset(altanims, 0, sizeof(altanims));
885 for (j = i;j < m->nummiptex;j++)
887 tx2 = loadmodel->brushq1.textures + j;
888 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
892 if (num >= '0' && num <= '9')
893 anims[num - '0'] = tx2;
894 else if (num >= 'a' && num <= 'j')
895 altanims[num - 'a'] = tx2;
897 Con_Printf("Bad animating texture %s\n", tx->name);
901 for (j = 0;j < 10;j++)
908 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
911 for (j = 0;j < max;j++)
915 Con_Printf("Missing frame %i of %s\n", j, tx->name);
919 for (j = 0;j < altmax;j++)
923 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
932 // if there is no alternate animation, duplicate the primary
933 // animation into the alternate
935 for (k = 0;k < 10;k++)
936 altanims[k] = anims[k];
939 // link together the primary animation
940 for (j = 0;j < max;j++)
943 tx2->animated = true;
944 tx2->anim_total[0] = max;
945 tx2->anim_total[1] = altmax;
946 for (k = 0;k < 10;k++)
948 tx2->anim_frames[0][k] = anims[k];
949 tx2->anim_frames[1][k] = altanims[k];
953 // if there really is an alternate anim...
954 if (anims[0] != altanims[0])
956 // link together the alternate animation
957 for (j = 0;j < altmax;j++)
960 tx2->animated = true;
961 // the primary/alternate are reversed here
962 tx2->anim_total[0] = altmax;
963 tx2->anim_total[1] = max;
964 for (k = 0;k < 10;k++)
966 tx2->anim_frames[0][k] = altanims[k];
967 tx2->anim_frames[1][k] = anims[k];
974 static void Mod_Q1BSP_LoadLighting(lump_t *l)
977 qbyte *in, *out, *data, d;
978 char litfilename[1024];
979 loadmodel->brushq1.lightdata = NULL;
980 if (loadmodel->brushq1.ishlbsp) // LordHavoc: load the colored lighting data straight
982 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
983 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
985 else // LordHavoc: bsp version 29 (normal white lighting)
987 // LordHavoc: hope is not lost yet, check for a .lit file to load
988 strcpy(litfilename, loadmodel->name);
989 FS_StripExtension(litfilename, litfilename);
990 strcat(litfilename, ".lit");
991 data = (qbyte*) FS_LoadFile(litfilename, false);
994 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
996 i = LittleLong(((int *)data)[1]);
999 Con_DPrintf("loaded %s\n", litfilename);
1000 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1001 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1007 Con_Printf("Unknown .lit file version (%d)\n", i);
1013 if (fs_filesize == 8)
1014 Con_Printf("Empty .lit file, ignoring\n");
1016 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1020 // LordHavoc: oh well, expand the white lighting data
1023 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1024 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1025 out = loadmodel->brushq1.lightdata;
1026 memcpy(in, mod_base + l->fileofs, l->filelen);
1027 for (i = 0;i < l->filelen;i++)
1037 static void Mod_Q1BSP_LoadLightList(void)
1039 int a, n, numlights;
1040 char lightsfilename[1024], *s, *t, *lightsstring;
1043 strcpy(lightsfilename, loadmodel->name);
1044 FS_StripExtension(lightsfilename, lightsfilename);
1045 strcat(lightsfilename, ".lights");
1046 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1052 while (*s && *s != '\n')
1056 Mem_Free(lightsstring);
1057 Host_Error("lights file must end with a newline\n");
1062 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1065 while (*s && n < numlights)
1068 while (*s && *s != '\n')
1072 Mem_Free(lightsstring);
1073 Host_Error("misparsed lights file!\n");
1075 e = loadmodel->brushq1.lights + n;
1077 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);
1081 Mem_Free(lightsstring);
1082 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);
1089 Mem_Free(lightsstring);
1090 Host_Error("misparsed lights file!\n");
1092 loadmodel->brushq1.numlights = numlights;
1093 Mem_Free(lightsstring);
1098 static int castshadowcount = 0;
1099 static void Mod_Q1BSP_ProcessLightList(void)
1101 int j, k, l, *mark, lnum;
1109 for (lnum = 0, e = loadmodel->brushq1.lights;lnum < loadmodel->brushq1.numlights;lnum++, e++)
1111 e->cullradius2 = DotProduct(e->light, e->light) / (e->falloff * e->falloff * 8192.0f * 8192.0f * 2.0f * 2.0f);// + 4096.0f;
1112 if (e->cullradius2 > 4096.0f * 4096.0f)
1113 e->cullradius2 = 4096.0f * 4096.0f;
1114 e->cullradius = e->lightradius = sqrt(e->cullradius2);
1115 leaf = Mod_Q1BSP_PointInLeaf(e->origin, loadmodel);
1116 if (leaf->compressed_vis)
1117 pvs = Mod_Q1BSP_DecompressVis(leaf->compressed_vis, loadmodel);
1119 pvs = mod_q1bsp_novis;
1120 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
1121 loadmodel->brushq1.surfacevisframes[j] = -1;
1122 for (j = 0, leaf = loadmodel->brushq1.leafs + 1;j < loadmodel->brushq1.numleafs - 1;j++, leaf++)
1124 if (pvs[j >> 3] & (1 << (j & 7)))
1126 for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++)
1128 surf = loadmodel->brushq1.surfaces + *mark;
1129 if (surf->number != *mark)
1130 Con_Printf("%d != %d\n", surf->number, *mark);
1131 dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1132 if (surf->flags & SURF_PLANEBACK)
1134 if (dist > 0 && dist < e->cullradius)
1136 temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
1137 temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
1138 temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
1139 if (DotProduct(temp, temp) < lightradius2)
1140 loadmodel->brushq1.surfacevisframes[*mark] = -2;
1145 // build list of light receiving surfaces
1147 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
1148 if (loadmodel->brushq1.surfacevisframes[j] == -2)
1151 if (e->numsurfaces > 0)
1153 e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
1155 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
1156 if (loadmodel->brushq1.surfacevisframes[j] == -2)
1157 e->surfaces[e->numsurfaces++] = loadmodel->brushq1.surfaces + j;
1159 // find bounding box and sphere of lit surfaces
1160 // (these will be used for creating a shape to clip the light)
1162 for (j = 0;j < e->numsurfaces;j++)
1164 surf = e->surfaces[j];
1167 VectorCopy(surf->poly_verts, e->mins);
1168 VectorCopy(surf->poly_verts, e->maxs);
1170 for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
1172 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
1173 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
1174 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
1175 VectorSubtract(v, e->origin, temp);
1176 dist = DotProduct(temp, temp);
1181 if (e->cullradius2 > radius2)
1183 e->cullradius2 = radius2;
1184 e->cullradius = sqrt(e->cullradius2);
1186 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
1187 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
1188 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
1189 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
1190 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
1191 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
1192 // clip shadow volumes against eachother to remove unnecessary
1193 // polygons(and sections of polygons)
1195 //vec3_t polymins, polymaxs;
1197 float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1198 float f, *v0, *v1, projectdistance;
1200 e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 1024);
1203 vec3_t outermins, outermaxs, innermins, innermaxs;
1204 innermins[0] = e->mins[0] - 1;
1205 innermins[1] = e->mins[1] - 1;
1206 innermins[2] = e->mins[2] - 1;
1207 innermaxs[0] = e->maxs[0] + 1;
1208 innermaxs[1] = e->maxs[1] + 1;
1209 innermaxs[2] = e->maxs[2] + 1;
1210 outermins[0] = loadmodel->normalmins[0] - 1;
1211 outermins[1] = loadmodel->normalmins[1] - 1;
1212 outermins[2] = loadmodel->normalmins[2] - 1;
1213 outermaxs[0] = loadmodel->normalmaxs[0] + 1;
1214 outermaxs[1] = loadmodel->normalmaxs[1] + 1;
1215 outermaxs[2] = loadmodel->normalmaxs[2] + 1;
1216 // add bounding box around the whole shadow volume set,
1217 // facing inward to limit light area, with an outer bounding box
1218 // facing outward (this is needed by the shadow rendering method)
1220 verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1221 verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1222 verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1223 verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1224 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1225 verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1226 verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1227 verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1228 verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1229 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1231 verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1232 verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1233 verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1234 verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1235 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1236 verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1237 verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1238 verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1239 verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1240 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1242 verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1243 verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1244 verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1245 verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1246 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1247 verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1248 verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1249 verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1250 verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1251 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1253 verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1254 verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1255 verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1256 verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1257 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1258 verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1259 verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1260 verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1261 verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1262 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1264 verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1265 verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
1266 verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
1267 verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1268 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1269 verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1270 verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
1271 verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
1272 verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1273 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1275 verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
1276 verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1277 verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1278 verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
1279 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1280 verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
1281 verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1282 verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1283 verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
1284 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1288 for (j = 0;j < e->numsurfaces;j++)
1290 surf = e->surfaces[j];
1291 if (surf->flags & SURF_SHADOWCAST)
1292 surf->castshadow = castshadowcount;
1294 for (j = 0;j < e->numsurfaces;j++)
1296 surf = e->surfaces[j];
1297 if (surf->castshadow != castshadowcount)
1299 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1300 if (surf->flags & SURF_PLANEBACK)
1302 projectdistance = e->lightradius;
1303 if (maxverts < surf->poly_numverts)
1305 maxverts = surf->poly_numverts;
1308 verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1310 // copy the original polygon, for the front cap of the volume
1311 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1313 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1314 // project the original polygon, reversed, for the back cap of the volume
1315 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1317 VectorSubtract(v0, e->origin, temp);
1318 VectorNormalize(temp);
1319 VectorMA(v0, projectdistance, temp, v1);
1321 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1322 // project the shadow volume sides
1323 for (l = surf->poly_numverts - 1, k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;l = k, k++, v0 = v1, v1 += 3)
1325 if (!surf->neighborsurfaces[l] || surf->neighborsurfaces[l]->castshadow != castshadowcount)
1327 VectorCopy(v1, &verts[0]);
1328 VectorCopy(v0, &verts[3]);
1329 VectorCopy(v0, &verts[6]);
1330 VectorCopy(v1, &verts[9]);
1331 VectorSubtract(&verts[6], e->origin, temp);
1332 VectorNormalize(temp);
1333 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1334 VectorSubtract(&verts[9], e->origin, temp);
1335 VectorNormalize(temp);
1336 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1337 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1341 // build the triangle mesh
1342 e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
1346 for (mesh = e->shadowvolume;mesh;mesh = mesh->next)
1347 l += mesh->numtriangles;
1348 Con_Printf("light %i shadow volume built containing %i triangles\n", lnum, l);
1356 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1358 loadmodel->brushq1.num_compressedpvs = 0;
1359 loadmodel->brushq1.data_compressedpvs = NULL;
1362 loadmodel->brushq1.num_compressedpvs = l->filelen;
1363 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1364 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1367 // used only for HalfLife maps
1368 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1370 char key[128], value[4096];
1375 if (!COM_ParseToken(&data, false))
1377 if (com_token[0] != '{')
1381 if (!COM_ParseToken(&data, false))
1383 if (com_token[0] == '}')
1384 break; // end of worldspawn
1385 if (com_token[0] == '_')
1386 strcpy(key, com_token + 1);
1388 strcpy(key, com_token);
1389 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1390 key[strlen(key)-1] = 0;
1391 if (!COM_ParseToken(&data, false))
1393 strcpy(value, com_token);
1394 if (!strcmp("wad", key)) // for HalfLife maps
1396 if (loadmodel->brushq1.ishlbsp)
1399 for (i = 0;i < 4096;i++)
1400 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1406 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1407 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1409 else if (value[i] == ';' || value[i] == 0)
1413 strcpy(wadname, "textures/");
1414 strcat(wadname, &value[j]);
1415 W_LoadTextureWadFile(wadname, false);
1427 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1429 loadmodel->brush.entities = NULL;
1432 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1433 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1434 if (loadmodel->brushq1.ishlbsp)
1435 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1439 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1445 in = (void *)(mod_base + l->fileofs);
1446 if (l->filelen % sizeof(*in))
1447 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1448 count = l->filelen / sizeof(*in);
1449 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1451 loadmodel->brushq1.vertexes = out;
1452 loadmodel->brushq1.numvertexes = count;
1454 for ( i=0 ; i<count ; i++, in++, out++)
1456 out->position[0] = LittleFloat(in->point[0]);
1457 out->position[1] = LittleFloat(in->point[1]);
1458 out->position[2] = LittleFloat(in->point[2]);
1462 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1468 in = (void *)(mod_base + l->fileofs);
1469 if (l->filelen % sizeof(*in))
1470 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1471 count = l->filelen / sizeof(*in);
1472 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1474 loadmodel->brushq1.submodels = out;
1475 loadmodel->brushq1.numsubmodels = count;
1477 for ( i=0 ; i<count ; i++, in++, out++)
1479 for (j=0 ; j<3 ; j++)
1481 // spread the mins / maxs by a pixel
1482 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1483 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1484 out->origin[j] = LittleFloat(in->origin[j]);
1486 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1487 out->headnode[j] = LittleLong(in->headnode[j]);
1488 out->visleafs = LittleLong(in->visleafs);
1489 out->firstface = LittleLong(in->firstface);
1490 out->numfaces = LittleLong(in->numfaces);
1494 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1500 in = (void *)(mod_base + l->fileofs);
1501 if (l->filelen % sizeof(*in))
1502 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1503 count = l->filelen / sizeof(*in);
1504 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1506 loadmodel->brushq1.edges = out;
1507 loadmodel->brushq1.numedges = count;
1509 for ( i=0 ; i<count ; i++, in++, out++)
1511 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1512 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1516 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1520 int i, j, k, count, miptex;
1522 in = (void *)(mod_base + l->fileofs);
1523 if (l->filelen % sizeof(*in))
1524 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1525 count = l->filelen / sizeof(*in);
1526 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1528 loadmodel->brushq1.texinfo = out;
1529 loadmodel->brushq1.numtexinfo = count;
1531 for (i = 0;i < count;i++, in++, out++)
1533 for (k = 0;k < 2;k++)
1534 for (j = 0;j < 4;j++)
1535 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1537 miptex = LittleLong(in->miptex);
1538 out->flags = LittleLong(in->flags);
1540 out->texture = NULL;
1541 if (loadmodel->brushq1.textures)
1543 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1544 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1546 out->texture = loadmodel->brushq1.textures + miptex;
1548 if (out->flags & TEX_SPECIAL)
1550 // if texture chosen is NULL or the shader needs a lightmap,
1551 // force to notexture water shader
1552 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1553 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1557 // if texture chosen is NULL, force to notexture
1558 if (out->texture == NULL)
1559 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1565 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1570 mins[0] = mins[1] = mins[2] = 9999;
1571 maxs[0] = maxs[1] = maxs[2] = -9999;
1573 for (i = 0;i < numverts;i++)
1575 for (j = 0;j < 3;j++, v++)
1585 #define MAX_SUBDIVPOLYTRIANGLES 4096
1586 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1588 static int subdivpolyverts, subdivpolytriangles;
1589 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1590 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1592 static int subdivpolylookupvert(vec3_t v)
1595 for (i = 0;i < subdivpolyverts;i++)
1596 if (subdivpolyvert[i][0] == v[0]
1597 && subdivpolyvert[i][1] == v[1]
1598 && subdivpolyvert[i][2] == v[2])
1600 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1601 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1602 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1603 return subdivpolyverts++;
1606 static void SubdividePolygon(int numverts, float *verts)
1608 int i, i1, i2, i3, f, b, c, p;
1609 vec3_t mins, maxs, front[256], back[256];
1610 float m, *pv, *cv, dist[256], frac;
1613 Host_Error("SubdividePolygon: ran out of verts in buffer");
1615 BoundPoly(numverts, verts, mins, maxs);
1617 for (i = 0;i < 3;i++)
1619 m = (mins[i] + maxs[i]) * 0.5;
1620 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1621 if (maxs[i] - m < 8)
1623 if (m - mins[i] < 8)
1627 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1628 dist[c] = cv[i] - m;
1631 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1635 VectorCopy(pv, front[f]);
1640 VectorCopy(pv, back[b]);
1643 if (dist[p] == 0 || dist[c] == 0)
1645 if ((dist[p] > 0) != (dist[c] > 0) )
1648 frac = dist[p] / (dist[p] - dist[c]);
1649 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1650 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1651 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1657 SubdividePolygon(f, front[0]);
1658 SubdividePolygon(b, back[0]);
1662 i1 = subdivpolylookupvert(verts);
1663 i2 = subdivpolylookupvert(verts + 3);
1664 for (i = 2;i < numverts;i++)
1666 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1668 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1672 i3 = subdivpolylookupvert(verts + i * 3);
1673 subdivpolyindex[subdivpolytriangles][0] = i1;
1674 subdivpolyindex[subdivpolytriangles][1] = i2;
1675 subdivpolyindex[subdivpolytriangles][2] = i3;
1677 subdivpolytriangles++;
1681 //Breaks a polygon up along axial 64 unit
1682 //boundaries so that turbulent and sky warps
1683 //can be done reasonably.
1684 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1690 subdivpolytriangles = 0;
1691 subdivpolyverts = 0;
1692 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1693 if (subdivpolytriangles < 1)
1694 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1696 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1697 mesh->numverts = subdivpolyverts;
1698 mesh->numtriangles = subdivpolytriangles;
1699 mesh->vertex = (surfvertex_t *)(mesh + 1);
1700 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1701 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1703 for (i = 0;i < mesh->numtriangles;i++)
1704 for (j = 0;j < 3;j++)
1705 mesh->index[i*3+j] = subdivpolyindex[i][j];
1707 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1709 VectorCopy(subdivpolyvert[i], v->v);
1710 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1711 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1716 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1719 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1720 mesh->numverts = numverts;
1721 mesh->numtriangles = numtriangles;
1722 mesh->vertex3f = (float *)(mesh + 1);
1723 mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1724 mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1725 mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1726 mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1727 mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1728 mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1729 mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1730 mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1731 mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1735 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1738 float *vec, *vert, mins[3], maxs[3], val, *v;
1741 // convert edges back to a normal polygon
1742 surf->poly_numverts = numedges;
1743 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1744 for (i = 0;i < numedges;i++)
1746 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1748 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1750 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1751 VectorCopy(vec, vert);
1755 // calculate polygon bounding box and center
1756 vert = surf->poly_verts;
1757 VectorCopy(vert, mins);
1758 VectorCopy(vert, maxs);
1760 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1762 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1763 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1764 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1766 VectorCopy(mins, surf->poly_mins);
1767 VectorCopy(maxs, surf->poly_maxs);
1768 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1769 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1770 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1772 // generate surface extents information
1773 tex = surf->texinfo;
1774 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1775 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1776 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1778 for (j = 0;j < 2;j++)
1780 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1787 for (i = 0;i < 2;i++)
1789 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1790 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1794 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1798 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1802 in = (void *)(mod_base + l->fileofs);
1803 if (l->filelen % sizeof(*in))
1804 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1805 count = l->filelen / sizeof(*in);
1806 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1808 loadmodel->brushq1.numsurfaces = count;
1809 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1810 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1811 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1813 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++)
1815 surf->number = surfnum;
1816 // FIXME: validate edges, texinfo, etc?
1817 firstedge = LittleLong(in->firstedge);
1818 numedges = LittleShort(in->numedges);
1819 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)
1820 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1821 i = LittleShort(in->texinfo);
1822 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1823 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1824 surf->texinfo = loadmodel->brushq1.texinfo + i;
1825 surf->flags = surf->texinfo->texture->flags;
1827 planenum = LittleShort(in->planenum);
1828 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1829 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1831 if (LittleShort(in->side))
1832 surf->flags |= SURF_PLANEBACK;
1834 surf->plane = loadmodel->brushq1.planes + planenum;
1836 // clear lightmap (filled in later)
1837 surf->lightmaptexture = NULL;
1839 // force lightmap upload on first time seeing the surface
1840 surf->cached_dlight = true;
1842 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1844 ssize = (surf->extents[0] >> 4) + 1;
1845 tsize = (surf->extents[1] >> 4) + 1;
1848 for (i = 0;i < MAXLIGHTMAPS;i++)
1849 surf->styles[i] = in->styles[i];
1850 i = LittleLong(in->lightofs);
1852 surf->samples = NULL;
1853 else if (loadmodel->brushq1.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1854 surf->samples = loadmodel->brushq1.lightdata + i;
1855 else // LordHavoc: white lighting (bsp version 29)
1856 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1858 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1860 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1861 Host_Error("Bad surface extents");
1862 // stainmap for permanent marks on walls
1863 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1865 memset(surf->stainsamples, 255, ssize * tsize * 3);
1869 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1870 loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1872 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++)
1874 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1875 mesh->numverts = surf->poly_numverts;
1876 mesh->numtriangles = surf->poly_numverts - 2;
1877 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1878 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1879 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1880 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1881 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1882 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1883 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1884 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1885 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1886 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1888 surf->lightmaptexturestride = 0;
1889 surf->lightmaptexture = NULL;
1891 for (i = 0;i < mesh->numverts;i++)
1893 mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1894 mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1895 mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1896 s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1897 t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1898 mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1899 mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1900 mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1901 mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1902 mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1903 mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1904 mesh->lightmapoffsets[i] = 0;
1907 for (i = 0;i < mesh->numtriangles;i++)
1909 mesh->element3i[i * 3 + 0] = 0;
1910 mesh->element3i[i * 3 + 1] = i + 1;
1911 mesh->element3i[i * 3 + 2] = i + 2;
1914 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1915 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1917 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1919 int i, iu, iv, smax, tmax;
1920 float u, v, ubase, vbase, uscale, vscale;
1922 smax = surf->extents[0] >> 4;
1923 tmax = surf->extents[1] >> 4;
1925 surf->flags |= SURF_LIGHTMAP;
1926 if (r_miplightmaps.integer)
1928 surf->lightmaptexturestride = smax+1;
1929 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);
1933 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1934 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);
1936 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1937 uscale = (uscale - ubase) / (smax + 1);
1938 vscale = (vscale - vbase) / (tmax + 1);
1940 for (i = 0;i < mesh->numverts;i++)
1942 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1943 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1944 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1945 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1946 // LordHavoc: calc lightmap data offset for vertex lighting to use
1949 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1955 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1957 node->parent = parent;
1958 if (node->contents < 0)
1960 Mod_Q1BSP_SetParent(node->children[0], node);
1961 Mod_Q1BSP_SetParent(node->children[1], node);
1964 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1970 in = (void *)(mod_base + l->fileofs);
1971 if (l->filelen % sizeof(*in))
1972 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1973 count = l->filelen / sizeof(*in);
1974 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1976 loadmodel->brushq1.nodes = out;
1977 loadmodel->brushq1.numnodes = count;
1979 for ( i=0 ; i<count ; i++, in++, out++)
1981 for (j=0 ; j<3 ; j++)
1983 out->mins[j] = LittleShort(in->mins[j]);
1984 out->maxs[j] = LittleShort(in->maxs[j]);
1987 p = LittleLong(in->planenum);
1988 out->plane = loadmodel->brushq1.planes + p;
1990 out->firstsurface = LittleShort(in->firstface);
1991 out->numsurfaces = LittleShort(in->numfaces);
1993 for (j=0 ; j<2 ; j++)
1995 p = LittleShort(in->children[j]);
1997 out->children[j] = loadmodel->brushq1.nodes + p;
1999 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
2003 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
2006 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
2010 int i, j, count, p, pvschainbytes;
2013 in = (void *)(mod_base + l->fileofs);
2014 if (l->filelen % sizeof(*in))
2015 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2016 count = l->filelen / sizeof(*in);
2017 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2019 loadmodel->brushq1.leafs = out;
2020 loadmodel->brushq1.numleafs = count;
2021 pvschainbytes = ((loadmodel->brushq1.num_leafs - 1)+7)>>3;
2022 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
2024 for ( i=0 ; i<count ; i++, in++, out++)
2026 for (j=0 ; j<3 ; j++)
2028 out->mins[j] = LittleShort(in->mins[j]);
2029 out->maxs[j] = LittleShort(in->maxs[j]);
2032 // FIXME: this function could really benefit from some error checking
2034 out->contents = LittleLong(in->contents);
2036 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
2037 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
2040 pvs += pvschainbytes;
2042 p = LittleLong(in->visofs);
2044 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
2046 memset(out->pvsdata, 0xFF, pvschainbytes);
2048 for (j = 0;j < 4;j++)
2049 out->ambient_sound_level[j] = in->ambient_level[j];
2051 // FIXME: Insert caustics here
2055 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
2057 dclipnode_t *in, *out;
2061 in = (void *)(mod_base + l->fileofs);
2062 if (l->filelen % sizeof(*in))
2063 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2064 count = l->filelen / sizeof(*in);
2065 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2067 loadmodel->brushq1.clipnodes = out;
2068 loadmodel->brushq1.numclipnodes = count;
2070 if (loadmodel->brushq1.ishlbsp)
2072 hull = &loadmodel->brushq1.hulls[1];
2073 hull->clipnodes = out;
2074 hull->firstclipnode = 0;
2075 hull->lastclipnode = count-1;
2076 hull->planes = loadmodel->brushq1.planes;
2077 hull->clip_mins[0] = -16;
2078 hull->clip_mins[1] = -16;
2079 hull->clip_mins[2] = -36;
2080 hull->clip_maxs[0] = 16;
2081 hull->clip_maxs[1] = 16;
2082 hull->clip_maxs[2] = 36;
2083 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2085 hull = &loadmodel->brushq1.hulls[2];
2086 hull->clipnodes = out;
2087 hull->firstclipnode = 0;
2088 hull->lastclipnode = count-1;
2089 hull->planes = loadmodel->brushq1.planes;
2090 hull->clip_mins[0] = -32;
2091 hull->clip_mins[1] = -32;
2092 hull->clip_mins[2] = -32;
2093 hull->clip_maxs[0] = 32;
2094 hull->clip_maxs[1] = 32;
2095 hull->clip_maxs[2] = 32;
2096 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2098 hull = &loadmodel->brushq1.hulls[3];
2099 hull->clipnodes = out;
2100 hull->firstclipnode = 0;
2101 hull->lastclipnode = count-1;
2102 hull->planes = loadmodel->brushq1.planes;
2103 hull->clip_mins[0] = -16;
2104 hull->clip_mins[1] = -16;
2105 hull->clip_mins[2] = -18;
2106 hull->clip_maxs[0] = 16;
2107 hull->clip_maxs[1] = 16;
2108 hull->clip_maxs[2] = 18;
2109 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2113 hull = &loadmodel->brushq1.hulls[1];
2114 hull->clipnodes = out;
2115 hull->firstclipnode = 0;
2116 hull->lastclipnode = count-1;
2117 hull->planes = loadmodel->brushq1.planes;
2118 hull->clip_mins[0] = -16;
2119 hull->clip_mins[1] = -16;
2120 hull->clip_mins[2] = -24;
2121 hull->clip_maxs[0] = 16;
2122 hull->clip_maxs[1] = 16;
2123 hull->clip_maxs[2] = 32;
2124 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2126 hull = &loadmodel->brushq1.hulls[2];
2127 hull->clipnodes = out;
2128 hull->firstclipnode = 0;
2129 hull->lastclipnode = count-1;
2130 hull->planes = loadmodel->brushq1.planes;
2131 hull->clip_mins[0] = -32;
2132 hull->clip_mins[1] = -32;
2133 hull->clip_mins[2] = -24;
2134 hull->clip_maxs[0] = 32;
2135 hull->clip_maxs[1] = 32;
2136 hull->clip_maxs[2] = 64;
2137 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2140 for (i=0 ; i<count ; i++, out++, in++)
2142 out->planenum = LittleLong(in->planenum);
2143 out->children[0] = LittleShort(in->children[0]);
2144 out->children[1] = LittleShort(in->children[1]);
2145 if (out->children[0] >= count || out->children[1] >= count)
2146 Host_Error("Corrupt clipping hull(out of range child)\n");
2150 //Duplicate the drawing hull structure as a clipping hull
2151 static void Mod_Q1BSP_MakeHull0(void)
2158 hull = &loadmodel->brushq1.hulls[0];
2160 in = loadmodel->brushq1.nodes;
2161 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
2163 hull->clipnodes = out;
2164 hull->firstclipnode = 0;
2165 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
2166 hull->planes = loadmodel->brushq1.planes;
2168 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
2170 out->planenum = in->plane - loadmodel->brushq1.planes;
2171 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
2172 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
2176 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
2181 in = (void *)(mod_base + l->fileofs);
2182 if (l->filelen % sizeof(*in))
2183 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2184 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2185 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2187 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2189 j = (unsigned) LittleShort(in[i]);
2190 if (j >= loadmodel->brushq1.numsurfaces)
2191 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2192 loadmodel->brushq1.marksurfaces[i] = j;
2196 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2201 in = (void *)(mod_base + l->fileofs);
2202 if (l->filelen % sizeof(*in))
2203 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2204 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2205 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2207 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2208 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2212 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2218 in = (void *)(mod_base + l->fileofs);
2219 if (l->filelen % sizeof(*in))
2220 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2222 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2223 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2225 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2227 out->normal[0] = LittleFloat(in->normal[0]);
2228 out->normal[1] = LittleFloat(in->normal[1]);
2229 out->normal[2] = LittleFloat(in->normal[2]);
2230 out->dist = LittleFloat(in->dist);
2236 #define MAX_POINTS_ON_WINDING 64
2242 double points[8][3]; // variable sized
2251 static winding_t *NewWinding(int points)
2256 if (points > MAX_POINTS_ON_WINDING)
2257 Sys_Error("NewWinding: too many points\n");
2259 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
2260 w = Mem_Alloc(loadmodel->mempool, size);
2266 static void FreeWinding(winding_t *w)
2276 static winding_t *BaseWindingForPlane(mplane_t *p)
2278 double org[3], vright[3], vup[3], normal[3];
2281 VectorCopy(p->normal, normal);
2282 VectorVectorsDouble(normal, vright, vup);
2284 VectorScale(vup, 1024.0*1024.0*1024.0, vup);
2285 VectorScale(vright, 1024.0*1024.0*1024.0, vright);
2287 // project a really big axis aligned box onto the plane
2290 VectorScale(p->normal, p->dist, org);
2292 VectorSubtract(org, vright, w->points[0]);
2293 VectorAdd(w->points[0], vup, w->points[0]);
2295 VectorAdd(org, vright, w->points[1]);
2296 VectorAdd(w->points[1], vup, w->points[1]);
2298 VectorAdd(org, vright, w->points[2]);
2299 VectorSubtract(w->points[2], vup, w->points[2]);
2301 VectorSubtract(org, vright, w->points[3]);
2302 VectorSubtract(w->points[3], vup, w->points[3]);
2313 Clips the winding to the plane, returning the new winding on the positive side
2314 Frees the input winding.
2315 If keepon is true, an exactly on-plane winding will be saved, otherwise
2316 it will be clipped away.
2319 static winding_t *ClipWinding(winding_t *in, mplane_t *split, int keepon)
2321 double dists[MAX_POINTS_ON_WINDING + 1];
2322 int sides[MAX_POINTS_ON_WINDING + 1];
2331 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2333 // determine sides for each point
2334 for (i = 0;i < in->numpoints;i++)
2336 dists[i] = dot = DotProduct(in->points[i], split->normal) - split->dist;
2337 if (dot > ON_EPSILON)
2338 sides[i] = SIDE_FRONT;
2339 else if (dot < -ON_EPSILON)
2340 sides[i] = SIDE_BACK;
2345 sides[i] = sides[0];
2346 dists[i] = dists[0];
2348 if (keepon && !counts[0] && !counts[1])
2359 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2360 if (maxpts > MAX_POINTS_ON_WINDING)
2361 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2363 neww = NewWinding(maxpts);
2365 for (i = 0;i < in->numpoints;i++)
2367 if (neww->numpoints >= maxpts)
2368 Sys_Error("ClipWinding: points exceeded estimate");
2372 if (sides[i] == SIDE_ON)
2374 VectorCopy(p1, neww->points[neww->numpoints]);
2379 if (sides[i] == SIDE_FRONT)
2381 VectorCopy(p1, neww->points[neww->numpoints]);
2385 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2388 // generate a split point
2389 p2 = in->points[(i+1)%in->numpoints];
2391 dot = dists[i] / (dists[i]-dists[i+1]);
2392 for (j = 0;j < 3;j++)
2393 { // avoid round off error when possible
2394 if (split->normal[j] == 1)
2395 mid[j] = split->dist;
2396 else if (split->normal[j] == -1)
2397 mid[j] = -split->dist;
2399 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2402 VectorCopy(mid, neww->points[neww->numpoints]);
2406 // free the original winding
2417 Divides a winding by a plane, producing one or two windings. The
2418 original winding is not damaged or freed. If only on one side, the
2419 returned winding will be the input winding. If on both sides, two
2420 new windings will be created.
2423 static void DivideWinding(winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2425 double dists[MAX_POINTS_ON_WINDING + 1];
2426 int sides[MAX_POINTS_ON_WINDING + 1];
2435 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2437 // determine sides for each point
2438 for (i = 0;i < in->numpoints;i++)
2440 dot = DotProduct(in->points[i], split->normal);
2443 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2444 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2445 else sides[i] = SIDE_ON;
2448 sides[i] = sides[0];
2449 dists[i] = dists[0];
2451 *front = *back = NULL;
2464 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2466 if (maxpts > MAX_POINTS_ON_WINDING)
2467 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2469 *front = f = NewWinding(maxpts);
2470 *back = b = NewWinding(maxpts);
2472 for (i = 0;i < in->numpoints;i++)
2474 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2475 Sys_Error("DivideWinding: points exceeded estimate");
2479 if (sides[i] == SIDE_ON)
2481 VectorCopy(p1, f->points[f->numpoints]);
2483 VectorCopy(p1, b->points[b->numpoints]);
2488 if (sides[i] == SIDE_FRONT)
2490 VectorCopy(p1, f->points[f->numpoints]);
2493 else if (sides[i] == SIDE_BACK)
2495 VectorCopy(p1, b->points[b->numpoints]);
2499 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2502 // generate a split point
2503 p2 = in->points[(i+1)%in->numpoints];
2505 dot = dists[i] / (dists[i]-dists[i+1]);
2506 for (j = 0;j < 3;j++)
2507 { // avoid round off error when possible
2508 if (split->normal[j] == 1)
2509 mid[j] = split->dist;
2510 else if (split->normal[j] == -1)
2511 mid[j] = -split->dist;
2513 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2516 VectorCopy(mid, f->points[f->numpoints]);
2518 VectorCopy(mid, b->points[b->numpoints]);
2523 typedef struct portal_s
2526 mnode_t *nodes[2]; // [0] = front side of plane
2527 struct portal_s *next[2];
2529 struct portal_s *chain; // all portals are linked into a list
2533 static portal_t *portalchain;
2540 static portal_t *AllocPortal(void)
2543 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2544 p->chain = portalchain;
2549 static void FreePortal(portal_t *p)
2554 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2556 // calculate children first
2557 if (node->children[0]->contents >= 0)
2558 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2559 if (node->children[1]->contents >= 0)
2560 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2562 // make combined bounding box from children
2563 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2564 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2565 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2566 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2567 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2568 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2571 static void Mod_Q1BSP_FinalizePortals(void)
2573 int i, j, numportals, numpoints;
2574 portal_t *p, *pnext;
2577 mleaf_t *leaf, *endleaf;
2580 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2581 leaf = loadmodel->brushq1.leafs;
2582 endleaf = leaf + loadmodel->brushq1.numleafs;
2583 for (;leaf < endleaf;leaf++)
2585 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2586 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2593 for (i = 0;i < 2;i++)
2595 leaf = (mleaf_t *)p->nodes[i];
2597 for (j = 0;j < w->numpoints;j++)
2599 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2600 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2601 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2602 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2603 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2604 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2611 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2613 // tally up portal and point counts
2619 // note: this check must match the one below or it will usually corrupt memory
2620 // 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
2621 if (p->winding && p->nodes[0] != p->nodes[1]
2622 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2623 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2626 numpoints += p->winding->numpoints * 2;
2630 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2631 loadmodel->brushq1.numportals = numportals;
2632 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2633 loadmodel->brushq1.numportalpoints = numpoints;
2634 // clear all leaf portal chains
2635 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2636 loadmodel->brushq1.leafs[i].portals = NULL;
2637 // process all portals in the global portal chain, while freeing them
2638 portal = loadmodel->brushq1.portals;
2639 point = loadmodel->brushq1.portalpoints;
2648 // note: this check must match the one above or it will usually corrupt memory
2649 // 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
2650 if (p->nodes[0] != p->nodes[1]
2651 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2652 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2654 // first make the back to front portal(forward portal)
2655 portal->points = point;
2656 portal->numpoints = p->winding->numpoints;
2657 portal->plane.dist = p->plane.dist;
2658 VectorCopy(p->plane.normal, portal->plane.normal);
2659 portal->here = (mleaf_t *)p->nodes[1];
2660 portal->past = (mleaf_t *)p->nodes[0];
2662 for (j = 0;j < portal->numpoints;j++)
2664 VectorCopy(p->winding->points[j], point->position);
2667 PlaneClassify(&portal->plane);
2669 // link into leaf's portal chain
2670 portal->next = portal->here->portals;
2671 portal->here->portals = portal;
2673 // advance to next portal
2676 // then make the front to back portal(backward portal)
2677 portal->points = point;
2678 portal->numpoints = p->winding->numpoints;
2679 portal->plane.dist = -p->plane.dist;
2680 VectorNegate(p->plane.normal, portal->plane.normal);
2681 portal->here = (mleaf_t *)p->nodes[0];
2682 portal->past = (mleaf_t *)p->nodes[1];
2684 for (j = portal->numpoints - 1;j >= 0;j--)
2686 VectorCopy(p->winding->points[j], point->position);
2689 PlaneClassify(&portal->plane);
2691 // link into leaf's portal chain
2692 portal->next = portal->here->portals;
2693 portal->here->portals = portal;
2695 // advance to next portal
2698 FreeWinding(p->winding);
2710 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2713 Host_Error("AddPortalToNodes: NULL front node");
2715 Host_Error("AddPortalToNodes: NULL back node");
2716 if (p->nodes[0] || p->nodes[1])
2717 Host_Error("AddPortalToNodes: already included");
2718 // 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
2720 p->nodes[0] = front;
2721 p->next[0] = (portal_t *)front->portals;
2722 front->portals = (mportal_t *)p;
2725 p->next[1] = (portal_t *)back->portals;
2726 back->portals = (mportal_t *)p;
2731 RemovePortalFromNode
2734 static void RemovePortalFromNodes(portal_t *portal)
2738 void **portalpointer;
2740 for (i = 0;i < 2;i++)
2742 node = portal->nodes[i];
2744 portalpointer = (void **) &node->portals;
2749 Host_Error("RemovePortalFromNodes: portal not in leaf");
2753 if (portal->nodes[0] == node)
2755 *portalpointer = portal->next[0];
2756 portal->nodes[0] = NULL;
2758 else if (portal->nodes[1] == node)
2760 *portalpointer = portal->next[1];
2761 portal->nodes[1] = NULL;
2764 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2768 if (t->nodes[0] == node)
2769 portalpointer = (void **) &t->next[0];
2770 else if (t->nodes[1] == node)
2771 portalpointer = (void **) &t->next[1];
2773 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2778 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2781 mnode_t *front, *back, *other_node;
2782 mplane_t clipplane, *plane;
2783 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2784 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2786 // if a leaf, we're done
2790 plane = node->plane;
2792 front = node->children[0];
2793 back = node->children[1];
2795 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2797 // create the new portal by generating a polygon for the node plane,
2798 // and clipping it by all of the other portals(which came from nodes above this one)
2799 nodeportal = AllocPortal();
2800 nodeportal->plane = *node->plane;
2802 nodeportalwinding = BaseWindingForPlane(node->plane);
2803 side = 0; // shut up compiler warning
2804 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2806 clipplane = portal->plane;
2807 if (portal->nodes[0] == portal->nodes[1])
2808 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2809 if (portal->nodes[0] == node)
2811 else if (portal->nodes[1] == node)
2813 clipplane.dist = -clipplane.dist;
2814 VectorNegate(clipplane.normal, clipplane.normal);
2818 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2820 nodeportalwinding = ClipWinding(nodeportalwinding, &clipplane, true);
2821 if (!nodeportalwinding)
2823 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2828 if (nodeportalwinding)
2830 // if the plane was not clipped on all sides, there was an error
2831 nodeportal->winding = nodeportalwinding;
2832 AddPortalToNodes(nodeportal, front, back);
2835 // split the portals of this node along this node's plane and assign them to the children of this node
2836 // (migrating the portals downward through the tree)
2837 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2839 if (portal->nodes[0] == portal->nodes[1])
2840 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2841 if (portal->nodes[0] == node)
2843 else if (portal->nodes[1] == node)
2846 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2847 nextportal = portal->next[side];
2849 other_node = portal->nodes[!side];
2850 RemovePortalFromNodes(portal);
2852 // cut the portal into two portals, one on each side of the node plane
2853 DivideWinding(portal->winding, plane, &frontwinding, &backwinding);
2858 AddPortalToNodes(portal, back, other_node);
2860 AddPortalToNodes(portal, other_node, back);
2866 AddPortalToNodes(portal, front, other_node);
2868 AddPortalToNodes(portal, other_node, front);
2872 // the winding is split
2873 splitportal = AllocPortal();
2874 temp = splitportal->chain;
2875 *splitportal = *portal;
2876 splitportal->chain = temp;
2877 splitportal->winding = backwinding;
2878 FreeWinding(portal->winding);
2879 portal->winding = frontwinding;
2883 AddPortalToNodes(portal, front, other_node);
2884 AddPortalToNodes(splitportal, back, other_node);
2888 AddPortalToNodes(portal, other_node, front);
2889 AddPortalToNodes(splitportal, other_node, back);
2893 Mod_Q1BSP_RecursiveNodePortals(front);
2894 Mod_Q1BSP_RecursiveNodePortals(back);
2898 static void Mod_Q1BSP_MakePortals(void)
2901 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2902 Mod_Q1BSP_FinalizePortals();
2905 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2908 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2909 msurface_t *surf, *s;
2910 float *v0, *v1, *v2, *v3;
2911 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2912 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2913 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2915 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)
2917 if (surf->neighborsurfaces[vertnum])
2919 surf->neighborsurfaces[vertnum] = NULL;
2920 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2922 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2923 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2924 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2927 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2928 if (s->neighborsurfaces[vnum] == surf)
2930 if (vnum < s->poly_numverts)
2932 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)
2934 if (s->neighborsurfaces[vnum] == NULL
2935 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2936 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2938 surf->neighborsurfaces[vertnum] = s;
2939 s->neighborsurfaces[vnum] = surf;
2943 if (vnum < s->poly_numverts)
2951 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2953 int i, j, stylecounts[256], totalcount, remapstyles[256];
2955 memset(stylecounts, 0, sizeof(stylecounts));
2956 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2958 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2959 for (j = 0;j < MAXLIGHTMAPS;j++)
2960 stylecounts[surf->styles[j]]++;
2963 model->brushq1.light_styles = 0;
2964 for (i = 0;i < 255;i++)
2968 remapstyles[i] = model->brushq1.light_styles++;
2969 totalcount += stylecounts[i] + 1;
2974 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2975 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2976 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2977 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2978 model->brushq1.light_styles = 0;
2979 for (i = 0;i < 255;i++)
2981 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2983 for (i = 0;i < model->brushq1.light_styles;i++)
2985 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2986 j += stylecounts[model->brushq1.light_style[i]] + 1;
2988 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2990 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2991 for (j = 0;j < MAXLIGHTMAPS;j++)
2992 if (surf->styles[j] != 255)
2993 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2996 for (i = 0;i < model->brushq1.light_styles;i++)
2998 *model->brushq1.light_styleupdatechains[i] = NULL;
2999 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
3000 j += stylecounts[model->brushq1.light_style[i]] + 1;
3004 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
3007 for (i = 0;i < model->brushq1.numtextures;i++)
3008 model->brushq1.pvstexturechainslength[i] = 0;
3009 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
3011 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
3013 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
3014 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
3017 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
3019 if (model->brushq1.pvstexturechainslength[i])
3021 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
3022 j += model->brushq1.pvstexturechainslength[i] + 1;
3025 model->brushq1.pvstexturechains[i] = NULL;
3027 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
3028 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
3029 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
3030 for (i = 0;i < model->brushq1.numtextures;i++)
3032 if (model->brushq1.pvstexturechainslength[i])
3034 *model->brushq1.pvstexturechains[i] = NULL;
3035 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
3040 void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
3048 // if this is a leaf, accumulate the pvs bits
3049 if (node->contents < 0)
3051 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
3052 for (i = 0;i < pvsbytes;i++)
3053 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
3057 plane = node->plane;
3058 d = DotProduct(org, plane->normal) - plane->dist;
3060 node = node->children[0];
3061 else if (d < -radius)
3062 node = node->children[1];
3065 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
3066 node = node->children[1];
3071 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
3072 //of the given point.
3073 int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
3075 int bytes = ((model->brushq1.num_leafs - 1) + 7) >> 3;
3076 bytes = min(bytes, pvsbufferlength);
3077 memset(pvsbuffer, 0, bytes);
3078 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, sv.worldmodel->brushq1.nodes);
3082 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
3083 extern void R_Model_Brush_Draw(entity_render_t *ent);
3084 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
3085 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);
3086 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
3091 mempool_t *mainmempool;
3093 model_t *originalloadmodel;
3094 float dist, modelyawradius, modelradius, *vec;
3098 mod->type = mod_brush;
3100 header = (dheader_t *)buffer;
3102 i = LittleLong(header->version);
3103 if (i != BSPVERSION && i != 30)
3104 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
3105 mod->brushq1.ishlbsp = i == 30;
3107 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
3108 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
3109 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
3110 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
3111 mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
3112 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
3113 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
3115 if (loadmodel->isworldmodel)
3117 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
3118 // until we get a texture for it...
3122 // swap all the lumps
3123 mod_base = (qbyte *)header;
3125 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
3126 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3130 // store which lightmap format to use
3131 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3133 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
3134 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
3135 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
3136 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
3137 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
3138 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
3139 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
3140 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
3141 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
3142 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
3143 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
3144 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
3145 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
3146 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
3147 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
3149 if (mod->brushq1.data_compressedpvs)
3150 Mem_Free(mod->brushq1.data_compressedpvs);
3151 mod->brushq1.data_compressedpvs = NULL;
3152 mod->brushq1.num_compressedpvs = 0;
3154 Mod_Q1BSP_MakeHull0();
3155 Mod_Q1BSP_MakePortals();
3157 if (developer.integer)
3158 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.num_leafs - 1, loadmodel->brushq1.numportals);
3160 mod->numframes = 2; // regular and alternate animation
3162 mainmempool = mod->mempool;
3163 loadname = mod->name;
3165 Mod_Q1BSP_LoadLightList();
3166 originalloadmodel = loadmodel;
3169 // set up the submodels(FIXME: this is confusing)
3171 for (i = 0;i < mod->brushq1.numsubmodels;i++)
3173 bm = &mod->brushq1.submodels[i];
3175 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3176 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3178 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3179 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3182 mod->brushq1.firstmodelsurface = bm->firstface;
3183 mod->brushq1.nummodelsurfaces = bm->numfaces;
3185 // this gets altered below if sky is used
3186 mod->DrawSky = NULL;
3187 mod->Draw = R_Model_Brush_Draw;
3188 mod->DrawFakeShadow = NULL;
3189 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
3190 mod->DrawLight = R_Model_Brush_DrawLight;
3191 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
3192 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
3193 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
3194 Mod_Q1BSP_BuildPVSTextureChains(mod);
3195 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
3196 if (mod->brushq1.nummodelsurfaces)
3198 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3199 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3200 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3203 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
3205 // we only need to have a drawsky function if it is used(usually only on world model)
3206 if (surf->texinfo->texture->shader == &Cshader_sky)
3207 mod->DrawSky = R_Model_Brush_DrawSky;
3208 // LordHavoc: submodels always clip, even if water
3209 if (mod->brushq1.numsubmodels - 1)
3210 surf->flags |= SURF_SOLIDCLIP;
3211 // calculate bounding shapes
3212 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
3214 for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
3216 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3217 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3218 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3219 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3220 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3221 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3222 dist = vec[0]*vec[0]+vec[1]*vec[1];
3223 if (modelyawradius < dist)
3224 modelyawradius = dist;
3225 dist += vec[2]*vec[2];
3226 if (modelradius < dist)
3231 modelyawradius = sqrt(modelyawradius);
3232 modelradius = sqrt(modelradius);
3233 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3234 mod->yawmins[2] = mod->normalmins[2];
3235 mod->yawmaxs[2] = mod->normalmaxs[2];
3236 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3237 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3238 mod->radius = modelradius;
3239 mod->radius2 = modelradius * modelradius;
3243 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3244 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
3246 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
3248 mod->brushq1.numleafs = bm->visleafs;
3250 // LordHavoc: only register submodels if it is the world
3251 // (prevents bsp models from replacing world submodels)
3252 if (loadmodel->isworldmodel && i < (mod->brushq1.numsubmodels - 1))
3255 // duplicate the basic information
3256 sprintf(name, "*%i", i+1);
3257 loadmodel = Mod_FindName(name);
3259 strcpy(loadmodel->name, name);
3260 // textures and memory belong to the main model
3261 loadmodel->texturepool = NULL;
3262 loadmodel->mempool = NULL;
3267 loadmodel = originalloadmodel;
3268 //Mod_Q1BSP_ProcessLightList();
3271 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3275 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3282 in = (void *)(mod_base + l->fileofs);
3283 if (l->filelen % sizeof(*in))
3284 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3285 count = l->filelen / sizeof(*in);
3286 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3289 loadmodel->num = count;
3291 for (i = 0;i < count;i++, in++, out++)
3297 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3304 in = (void *)(mod_base + l->fileofs);
3305 if (l->filelen % sizeof(*in))
3306 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3307 count = l->filelen / sizeof(*in);
3308 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3311 loadmodel->num = count;
3313 for (i = 0;i < count;i++, in++, out++)
3319 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3326 in = (void *)(mod_base + l->fileofs);
3327 if (l->filelen % sizeof(*in))
3328 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3329 count = l->filelen / sizeof(*in);
3330 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3333 loadmodel->num = count;
3335 for (i = 0;i < count;i++, in++, out++)
3341 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3348 in = (void *)(mod_base + l->fileofs);
3349 if (l->filelen % sizeof(*in))
3350 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3351 count = l->filelen / sizeof(*in);
3352 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3355 loadmodel->num = count;
3357 for (i = 0;i < count;i++, in++, out++)
3363 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3370 in = (void *)(mod_base + l->fileofs);
3371 if (l->filelen % sizeof(*in))
3372 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3373 count = l->filelen / sizeof(*in);
3374 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3377 loadmodel->num = count;
3379 for (i = 0;i < count;i++, in++, out++)
3385 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3392 in = (void *)(mod_base + l->fileofs);
3393 if (l->filelen % sizeof(*in))
3394 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3395 count = l->filelen / sizeof(*in);
3396 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3399 loadmodel->num = count;
3401 for (i = 0;i < count;i++, in++, out++)
3407 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3414 in = (void *)(mod_base + l->fileofs);
3415 if (l->filelen % sizeof(*in))
3416 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3417 count = l->filelen / sizeof(*in);
3418 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3421 loadmodel->num = count;
3423 for (i = 0;i < count;i++, in++, out++)
3429 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3436 in = (void *)(mod_base + l->fileofs);
3437 if (l->filelen % sizeof(*in))
3438 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3439 count = l->filelen / sizeof(*in);
3440 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3443 loadmodel->num = count;
3445 for (i = 0;i < count;i++, in++, out++)
3451 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3458 in = (void *)(mod_base + l->fileofs);
3459 if (l->filelen % sizeof(*in))
3460 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3461 count = l->filelen / sizeof(*in);
3462 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3465 loadmodel->num = count;
3467 for (i = 0;i < count;i++, in++, out++)
3473 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3480 in = (void *)(mod_base + l->fileofs);
3481 if (l->filelen % sizeof(*in))
3482 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3483 count = l->filelen / sizeof(*in);
3484 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3487 loadmodel->num = count;
3489 for (i = 0;i < count;i++, in++, out++)
3495 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3502 in = (void *)(mod_base + l->fileofs);
3503 if (l->filelen % sizeof(*in))
3504 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3505 count = l->filelen / sizeof(*in);
3506 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3509 loadmodel->num = count;
3511 for (i = 0;i < count;i++, in++, out++)
3517 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3524 in = (void *)(mod_base + l->fileofs);
3525 if (l->filelen % sizeof(*in))
3526 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3527 count = l->filelen / sizeof(*in);
3528 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3531 loadmodel->num = count;
3533 for (i = 0;i < count;i++, in++, out++)
3539 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3546 in = (void *)(mod_base + l->fileofs);
3547 if (l->filelen % sizeof(*in))
3548 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3549 count = l->filelen / sizeof(*in);
3550 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3553 loadmodel->num = count;
3555 for (i = 0;i < count;i++, in++, out++)
3561 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3568 in = (void *)(mod_base + l->fileofs);
3569 if (l->filelen % sizeof(*in))
3570 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3571 count = l->filelen / sizeof(*in);
3572 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3575 loadmodel->num = count;
3577 for (i = 0;i < count;i++, in++, out++)
3583 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3590 in = (void *)(mod_base + l->fileofs);
3591 if (l->filelen % sizeof(*in))
3592 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3593 count = l->filelen / sizeof(*in);
3594 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3597 loadmodel->num = count;
3599 for (i = 0;i < count;i++, in++, out++)
3605 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3612 in = (void *)(mod_base + l->fileofs);
3613 if (l->filelen % sizeof(*in))
3614 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3615 count = l->filelen / sizeof(*in);
3616 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3619 loadmodel->num = count;
3621 for (i = 0;i < count;i++, in++, out++)
3627 static void Mod_Q2BSP_LoadModels(lump_t *l)
3634 in = (void *)(mod_base + l->fileofs);
3635 if (l->filelen % sizeof(*in))
3636 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3637 count = l->filelen / sizeof(*in);
3638 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3641 loadmodel->num = count;
3643 for (i = 0;i < count;i++, in++, out++)
3649 void Mod_Q2BSP_Load(model_t *mod, void *buffer)
3652 q2dheader_t *header;
3654 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3656 mod->type = mod_brushq2;
3658 header = (q2dheader_t *)buffer;
3660 i = LittleLong(header->version);
3661 if (i != Q2BSPVERSION)
3662 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3663 mod->brushq1.ishlbsp = false;
3664 if (loadmodel->isworldmodel)
3666 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
3667 // until we get a texture for it...
3671 mod_base = (qbyte *)header;
3673 // swap all the lumps
3674 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3675 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3677 // store which lightmap format to use
3678 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3680 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3681 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3682 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3683 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3684 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3685 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3686 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3687 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3688 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3689 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3690 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3691 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3692 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3693 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3694 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3695 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3696 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3697 // LordHavoc: must go last because this makes the submodels
3698 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3702 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3705 char key[128], value[4096];
3707 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3708 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3709 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3712 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3713 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3714 data = loadmodel->brush.entities;
3715 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3716 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3720 if (!COM_ParseToken(&data, false))
3722 if (com_token[0] == '}')
3723 break; // end of worldspawn
3724 if (com_token[0] == '_')
3725 strcpy(key, com_token + 1);
3727 strcpy(key, com_token);
3728 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3729 key[strlen(key)-1] = 0;
3730 if (!COM_ParseToken(&data, false))
3732 strcpy(value, com_token);
3733 if (!strcmp("gridsize", key))
3735 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3736 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3742 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3748 in = (void *)(mod_base + l->fileofs);
3749 if (l->filelen % sizeof(*in))
3750 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3751 count = l->filelen / sizeof(*in);
3752 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3754 loadmodel->brushq3.data_textures = out;
3755 loadmodel->brushq3.num_textures = count;
3757 for (i = 0;i < count;i++, in++, out++)
3759 strncpy(out->name, in->name, sizeof(out->name) - 1);
3760 out->surfaceflags = LittleLong(in->surfaceflags);
3761 out->contents = LittleLong(in->contents);
3764 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3768 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3774 in = (void *)(mod_base + l->fileofs);
3775 if (l->filelen % sizeof(*in))
3776 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3777 count = l->filelen / sizeof(*in);
3778 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3780 loadmodel->brushq3.data_planes = out;
3781 loadmodel->brushq3.num_planes = count;
3783 for (i = 0;i < count;i++, in++, out++)
3785 out->normal[0] = LittleLong(in->normal[0]);
3786 out->normal[1] = LittleLong(in->normal[1]);
3787 out->normal[2] = LittleLong(in->normal[2]);
3788 out->dist = LittleLong(in->dist);
3793 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3796 q3mbrushside_t *out;
3799 in = (void *)(mod_base + l->fileofs);
3800 if (l->filelen % sizeof(*in))
3801 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3802 count = l->filelen / sizeof(*in);
3803 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3805 loadmodel->brushq3.data_brushsides = out;
3806 loadmodel->brushq3.num_brushsides = count;
3808 for (i = 0;i < count;i++, in++, out++)
3810 n = LittleLong(in->planeindex);
3811 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3812 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3813 out->plane = loadmodel->brushq3.data_planes + n;
3814 n = LittleLong(in->textureindex);
3815 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3816 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3817 out->texture = loadmodel->brushq3.data_textures + n;
3821 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3825 int i, j, k, m, n, c, count, numpoints, numplanes;
3827 colpointf_t pointsbuf[256*3];
3828 colplanef_t planesbuf[256], colplanef;
3830 in = (void *)(mod_base + l->fileofs);
3831 if (l->filelen % sizeof(*in))
3832 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3833 count = l->filelen / sizeof(*in);
3834 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3836 loadmodel->brushq3.data_brushes = out;
3837 loadmodel->brushq3.num_brushes = count;
3839 for (i = 0;i < count;i++, in++, out++)
3841 n = LittleLong(in->firstbrushside);
3842 c = LittleLong(in->numbrushsides);
3843 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3844 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3845 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3846 out->numbrushsides = c;
3847 n = LittleLong(in->textureindex);
3848 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3849 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3850 out->texture = loadmodel->brushq3.data_textures + n;
3852 // construct a collision brush, which needs points and planes...
3853 // each point and plane should be unique, and they don't refer to
3854 // eachother in any way, so keeping them unique is fairly easy
3857 for (j = 0;j < out->numbrushsides;j++)
3859 // create a huge polygon for the plane
3860 w = BaseWindingForPlane(out->firstbrushside[j].plane);
3861 // clip it by all other planes
3862 for (k = 0;k < out->numbrushsides && w;k++)
3864 w = ClipWinding(w, out->firstbrushside[k].plane, true);
3865 // if nothing is left, skip it
3866 // FIXME: should keep count of how many were skipped and report
3867 // it, just for sake of statistics
3870 // add the points uniquely (no duplicates)
3871 for (k = 0;k < w->numpoints;k++)
3873 for (m = 0;m < numpoints;m++)
3874 if (VectorDistance2(w->points[k * 3], pointsbuf[m * 3].v) < DIST_EPSILON)
3878 // check if there are too many and skip the brush
3879 if (numpoints >= 256)
3881 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many points for buffer\n");
3883 goto failedtomakecolbrush;
3886 VectorCopy(w->points[k * 3], pointsbuf[numpoints * 3].v);
3890 // add the plane uniquely (no duplicates)
3891 memset(&colplanef, 0, sizeof(colplanef));
3892 VectorCopy(out->firstbrushside[k].plane->normal, colplanef.normal);
3893 colplanef.dist = out->firstbrushside[k].plane->dist;
3894 for (k = 0;k < numplanes;k++)
3895 if (VectorCompare(planesbuf[k].normal, colplanef.normal) && planesbuf[k].dist == colplanef.dist)
3899 // check if there are too many and skip the brush
3900 if (numplanes >= 256)
3902 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many planes for buffer\n");
3904 goto failedtomakecolbrush;
3907 planesbuf[numplanes++] = colplanef;
3911 // if anything is left, create the collision brush
3912 if (numplanes && numpoints)
3914 out->colbrushf = Collision_AllocBrushFloat(loadmodel->mempool, numpoints, numplanes);
3915 memcpy(out->colbrushf->points, pointsbuf, numpoints * sizeof(float[3]));
3916 memcpy(out->colbrushf->planes, planesbuf, numplanes * sizeof(mplane_t));
3918 // return from errors to here
3919 failedtomakecolbrush:;
3923 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3929 in = (void *)(mod_base + l->fileofs);
3930 if (l->filelen % sizeof(*in))
3931 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3932 count = l->filelen / sizeof(*in);
3933 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3935 loadmodel->brushq3.data_effects = out;
3936 loadmodel->brushq3.num_effects = count;
3938 for (i = 0;i < count;i++, in++, out++)
3940 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3941 n = LittleLong(in->brushindex);
3942 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3943 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3944 out->brush = loadmodel->brushq3.data_brushes + n;
3945 out->unknown = LittleLong(in->unknown);
3949 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3954 in = (void *)(mod_base + l->fileofs);
3955 if (l->filelen % sizeof(*in))
3956 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3957 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3958 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3959 loadmodel->brushq3.data_texturetexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3960 loadmodel->brushq3.data_lightmaptexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3961 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3962 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3963 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3964 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3966 for (i = 0;i < count;i++, in++)
3968 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3969 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3970 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3971 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3972 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3973 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3974 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3975 // svector/tvector are calculated later in face loading
3976 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3977 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3978 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3979 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3980 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3981 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3982 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3983 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3984 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3985 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3986 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3987 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3988 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3992 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3998 in = (void *)(mod_base + l->fileofs);
3999 if (l->filelen % sizeof(int[3]))
4000 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4001 count = l->filelen / sizeof(*in);
4002 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4004 loadmodel->brushq3.num_triangles = count / 3;
4005 loadmodel->brushq3.data_element3i = out;
4006 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4008 for (i = 0;i < count;i++, in++, out++)
4010 *out = LittleLong(*in);
4011 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4012 Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices);
4016 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
4022 in = (void *)(mod_base + l->fileofs);
4023 if (l->filelen % sizeof(*in))
4024 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4025 count = l->filelen / sizeof(*in);
4026 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4028 loadmodel->brushq3.data_lightmaps = out;
4029 loadmodel->brushq3.num_lightmaps = count;
4031 for (i = 0;i < count;i++, in++, out++)
4032 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4035 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4039 int i, j, n, count, invalidelements, patchsize[2];
4041 in = (void *)(mod_base + l->fileofs);
4042 if (l->filelen % sizeof(*in))
4043 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4044 count = l->filelen / sizeof(*in);
4045 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4047 loadmodel->brushq3.data_faces = out;
4048 loadmodel->brushq3.num_faces = count;
4050 for (i = 0;i < count;i++, in++, out++)
4052 // check face type first
4053 out->type = LittleLong(in->type);
4054 if (out->type != Q3FACETYPE_POLYGON
4055 && out->type != Q3FACETYPE_PATCH
4056 && out->type != Q3FACETYPE_MESH
4057 && out->type != Q3FACETYPE_FLARE)
4059 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
4060 out->type = 0; // error
4064 n = LittleLong(in->textureindex);
4065 if (n < 0 || n >= loadmodel->brushq3.num_textures)
4067 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
4068 out->type = 0; // error
4072 out->texture = loadmodel->brushq3.data_textures + n;
4073 n = LittleLong(in->effectindex);
4074 if (n < -1 || n >= loadmodel->brushq3.num_effects)
4076 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4082 out->effect = loadmodel->brushq3.data_effects + n;
4083 n = LittleLong(in->lightmapindex);
4084 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
4086 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4090 out->lightmaptexture = NULL;
4092 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4094 out->firstvertex = LittleLong(in->firstvertex);
4095 out->numvertices = LittleLong(in->numvertices);
4096 out->firstelement = LittleLong(in->firstelement);
4097 out->numelements = LittleLong(in->numelements);
4098 out->numtriangles = out->numelements / 3;
4099 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
4101 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->numvertices, loadmodel->brushq3.num_vertices);
4102 out->type = 0; // error
4105 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
4107 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->numelements, loadmodel->brushq3.num_triangles * 3);
4108 out->type = 0; // error
4111 if (out->numtriangles * 3 != out->numelements)
4113 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
4114 out->type = 0; // error
4119 case Q3FACETYPE_POLYGON:
4120 case Q3FACETYPE_MESH:
4121 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4122 out->data_texturetexcoord2f = loadmodel->brushq3.data_texturetexcoord2f + out->firstvertex * 2;
4123 out->data_lightmaptexcoord2f = loadmodel->brushq3.data_lightmaptexcoord2f + out->firstvertex * 2;
4124 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4125 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4126 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4127 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4128 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4129 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4131 case Q3FACETYPE_PATCH:
4132 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4133 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4134 if (patchsize[0] < 1 || patchsize[1] < 1)
4136 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4137 out->type = 0; // error
4140 // FIXME: convert patch to triangles here!
4141 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
4145 case Q3FACETYPE_FLARE:
4146 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4151 for (j = 0, invalidelements = 0;j < out->numelements;j++)
4152 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
4154 if (invalidelements)
4156 Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->contents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->contents, out->firstvertex, out->numvertices, out->firstelement, out->numelements);
4157 for (j = 0;j < out->numelements;j++)
4159 Con_Printf(" %i", out->data_element3i[j]);
4160 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
4161 out->data_element3i[j] = 0;
4168 static void Mod_Q3BSP_LoadModels(lump_t *l)
4172 int i, j, n, c, count;
4174 in = (void *)(mod_base + l->fileofs);
4175 if (l->filelen % sizeof(*in))
4176 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4177 count = l->filelen / sizeof(*in);
4178 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4180 loadmodel->brushq3.data_models = out;
4181 loadmodel->brushq3.num_models = count;
4183 for (i = 0;i < count;i++, in++, out++)
4185 for (j = 0;j < 3;j++)
4187 out->mins[j] = LittleFloat(in->mins[j]);
4188 out->maxs[j] = LittleFloat(in->maxs[j]);
4190 n = LittleLong(in->firstface);
4191 c = LittleLong(in->numfaces);
4192 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4193 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4194 out->firstface = loadmodel->brushq3.data_faces + n;
4196 n = LittleLong(in->firstbrush);
4197 c = LittleLong(in->numbrushes);
4198 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4199 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4200 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4201 out->numbrushes = c;
4205 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4211 in = (void *)(mod_base + l->fileofs);
4212 if (l->filelen % sizeof(*in))
4213 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4214 count = l->filelen / sizeof(*in);
4215 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4217 loadmodel->brushq3.data_leafbrushes = out;
4218 loadmodel->brushq3.num_leafbrushes = count;
4220 for (i = 0;i < count;i++, in++, out++)
4222 n = LittleLong(*in);
4223 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4224 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4225 *out = loadmodel->brushq3.data_brushes + n;
4229 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4235 in = (void *)(mod_base + l->fileofs);
4236 if (l->filelen % sizeof(*in))
4237 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4238 count = l->filelen / sizeof(*in);
4239 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4241 loadmodel->brushq3.data_leaffaces = out;
4242 loadmodel->brushq3.num_leaffaces = count;
4244 for (i = 0;i < count;i++, in++, out++)
4246 n = LittleLong(*in);
4247 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4248 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4249 *out = loadmodel->brushq3.data_faces + n;
4253 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4257 int i, j, n, c, count;
4259 in = (void *)(mod_base + l->fileofs);
4260 if (l->filelen % sizeof(*in))
4261 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4262 count = l->filelen / sizeof(*in);
4263 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4265 loadmodel->brushq3.data_leafs = out;
4266 loadmodel->brushq3.num_leafs = count;
4268 for (i = 0;i < count;i++, in++, out++)
4270 out->isnode = false;
4272 out->clusterindex = LittleLong(in->clusterindex);
4273 out->areaindex = LittleLong(in->areaindex);
4274 for (j = 0;j < 3;j++)
4276 // yes the mins/maxs are ints
4277 out->mins[j] = LittleLong(in->mins[j]);
4278 out->maxs[j] = LittleLong(in->maxs[j]);
4280 n = LittleLong(in->firstleafface);
4281 c = LittleLong(in->numleaffaces);
4282 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4283 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4284 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4285 out->numleaffaces = c;
4286 n = LittleLong(in->firstleafbrush);
4287 c = LittleLong(in->numleafbrushes);
4288 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4289 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4290 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4291 out->numleafbrushes = c;
4295 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4298 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4299 node->parent = parent;
4302 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4303 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4307 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4313 in = (void *)(mod_base + l->fileofs);
4314 if (l->filelen % sizeof(*in))
4315 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4316 count = l->filelen / sizeof(*in);
4317 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4319 loadmodel->brushq3.data_nodes = out;
4320 loadmodel->brushq3.num_nodes = count;
4322 for (i = 0;i < count;i++, in++, out++)
4326 n = LittleLong(in->planeindex);
4327 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4328 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4329 out->plane = loadmodel->brushq3.data_planes + n;
4330 for (j = 0;j < 2;j++)
4332 n = LittleLong(in->childrenindex[j]);
4335 if (n >= loadmodel->brushq3.num_nodes)
4336 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4337 out->children[j] = loadmodel->brushq3.data_nodes + n;
4342 if (n >= loadmodel->brushq3.num_leafs)
4343 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4344 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4347 // we don't load the mins/maxs
4350 // set the parent pointers
4351 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4354 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4357 q3dlightgrid_t *out;
4360 in = (void *)(mod_base + l->fileofs);
4361 if (l->filelen % sizeof(*in))
4362 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4363 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4364 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4365 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4366 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4367 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4368 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4369 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4370 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4371 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4372 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4373 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4374 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4375 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4376 if (l->filelen < count * (int)sizeof(*in))
4377 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]);
4378 if (l->filelen != count * (int)sizeof(*in))
4379 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4381 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4382 loadmodel->brushq3.data_lightgrid = out;
4383 loadmodel->brushq3.num_lightgrid = count;
4385 // no swapping or validation necessary
4386 memcpy(out, in, count * (int)sizeof(*out));
4388 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]);
4389 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]);
4392 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4397 in = (void *)(mod_base + l->fileofs);
4399 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4401 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4402 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4403 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4404 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4405 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4406 if (l->filelen < totalchains + (int)sizeof(*in))
4407 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);
4409 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4410 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4413 void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4415 // FIXME: finish this code
4416 VectorCopy(in, out);
4419 void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end)
4423 // recurse down node sides
4426 colpointf_t *ps, *pe;
4427 // FIXME? if TraceBrushPolygonTransform were to be made usable, the
4428 // node planes would need to be transformed too
4429 dist = node->plane->dist - (1.0f / 8.0f);
4430 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4432 if (DotProduct(ps->v, node->plane->normal) > dist || DotProduct(pe->v, node->plane->normal) > dist)
4434 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end);
4438 dist = node->plane->dist + (1.0f / 8.0f);
4439 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4441 if (DotProduct(ps->v, node->plane->normal) < dist || DotProduct(pe->v, node->plane->normal) < dist)
4443 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end);
4448 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4450 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4452 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4459 leaf = (q3mleaf_t *)node;
4460 for (i = 0;i < leaf->numleafbrushes;i++)
4461 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4465 void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4467 // FIXME: write this
4468 ambientcolor[0] += 255;
4469 ambientcolor[1] += 255;
4470 ambientcolor[2] += 255;
4473 void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs)
4476 colbrushf_t *thisbrush_start, *thisbrush_end;
4477 matrix4x4_t startmatrix, endmatrix;
4478 // FIXME: finish this code
4479 Matrix4x4_CreateIdentity(&startmatrix);
4480 Matrix4x4_CreateIdentity(&endmatrix);
4481 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4482 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4483 memset(trace, 0, sizeof(*trace));
4484 trace->fraction = 1;
4485 if (model->brushq3.num_nodes)
4486 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end);
4488 for (i = 0;i < model->brushq3.num_brushes;i++)
4489 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_brushes[i].colbrushf, model->brushq3.data_brushes[i].colbrushf);
4493 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)
4500 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4501 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4504 // node - recurse down the BSP tree
4505 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4508 node = node->children[0];
4511 node = node->children[1];
4513 default: // crossing
4514 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4516 node = node->children[1];
4523 int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4525 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4528 int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4530 // FIXME: write this
4531 memset(pvsbuffer, 0xFF, pvsbufferlength);
4532 return pvsbufferlength;
4535 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4538 q3dheader_t *header;
4540 mod->type = mod_brushq2;
4542 header = (q3dheader_t *)buffer;
4544 i = LittleLong(header->version);
4545 if (i != Q3BSPVERSION)
4546 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4547 if (loadmodel->isworldmodel)
4549 Cvar_SetValue("halflifebsp", false);
4550 // until we get a texture for it...
4554 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4555 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4556 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4557 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4558 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4559 //mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
4560 //mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
4562 mod_base = (qbyte *)header;
4564 // swap all the lumps
4565 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4566 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4568 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4569 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4570 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4571 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4572 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4573 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4574 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4575 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4576 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4577 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4578 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4579 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4580 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4581 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4582 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4583 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4584 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4587 void Mod_IBSP_Load(model_t *mod, void *buffer)
4589 int i = LittleLong(((int *)buffer)[1]);
4590 if (i == Q3BSPVERSION)
4591 Mod_Q3BSP_Load(mod,buffer);
4592 else if (i == Q2BSPVERSION)
4593 Mod_Q2BSP_Load(mod,buffer);
4595 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4598 void Mod_MAP_Load(model_t *mod, void *buffer)
4600 Host_Error("Mod_MAP_Load: not yet implemented\n");