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 qbyte *Mod_Q1BSP_DecompressVis(model_t *model, qbyte *in)
653 static qbyte decompressed[MAX_MAP_LEAFS/8];
658 row = (model->brushq1.numleafs+7)>>3;
676 } while (out - decompressed < row);
681 static qbyte *Mod_Q1BSP_LeafPVS(model_t *model, mleaf_t *leaf)
683 if (r_novis.integer || leaf == model->brushq1.leafs || leaf->compressed_vis == NULL)
684 return mod_q1bsp_novis;
685 return Mod_Q1BSP_DecompressVis(model, leaf->compressed_vis);
688 static void Mod_Q1BSP_LoadTextures(lump_t *l)
690 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
692 texture_t *tx, *tx2, *anims[10], *altanims[10];
694 qbyte *data, *mtdata;
697 loadmodel->brushq1.textures = NULL;
702 m = (dmiptexlump_t *)(mod_base + l->fileofs);
704 m->nummiptex = LittleLong (m->nummiptex);
706 // add two slots for notexture walls and notexture liquids
707 loadmodel->brushq1.numtextures = m->nummiptex + 2;
708 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
710 // fill out all slots with notexture
711 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
714 strcpy(tx->name, "NO TEXTURE FOUND");
717 tx->skin.base = r_notexture;
718 tx->shader = &Cshader_wall_lightmap;
719 tx->flags = SURF_SOLIDCLIP;
720 if (i == loadmodel->brushq1.numtextures - 1)
722 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
723 tx->shader = &Cshader_water;
725 tx->currentframe = tx;
728 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
730 // LordHavoc: mostly rewritten map texture loader
731 for (i = 0;i < m->nummiptex;i++)
733 dofs[i] = LittleLong(dofs[i]);
734 if (dofs[i] == -1 || r_nosurftextures.integer)
736 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
738 // make sure name is no more than 15 characters
739 for (j = 0;dmiptex->name[j] && j < 15;j++)
740 name[j] = dmiptex->name[j];
743 mtwidth = LittleLong(dmiptex->width);
744 mtheight = LittleLong(dmiptex->height);
746 j = LittleLong(dmiptex->offsets[0]);
750 if (j < 40 || j + mtwidth * mtheight > l->filelen)
752 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
755 mtdata = (qbyte *)dmiptex + j;
758 if ((mtwidth & 15) || (mtheight & 15))
759 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
761 // LordHavoc: force all names to lowercase
762 for (j = 0;name[j];j++)
763 if (name[j] >= 'A' && name[j] <= 'Z')
764 name[j] += 'a' - 'A';
766 tx = loadmodel->brushq1.textures + i;
767 strcpy(tx->name, name);
769 tx->height = mtheight;
773 sprintf(tx->name, "unnamed%i", i);
774 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
777 // LordHavoc: HL sky textures are entirely different than quake
778 if (!loadmodel->brushq1.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
780 if (loadmodel->isworldmodel)
782 data = loadimagepixels(tx->name, false, 0, 0);
785 if (image_width == 256 && image_height == 128)
793 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
795 R_InitSky(mtdata, 1);
798 else if (mtdata != NULL)
799 R_InitSky(mtdata, 1);
804 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
806 // did not find external texture, load it from the bsp or wad3
807 if (loadmodel->brushq1.ishlbsp)
809 // internal texture overrides wad
810 qbyte *pixels, *freepixels, *fogpixels;
811 pixels = freepixels = NULL;
813 pixels = W_ConvertWAD3Texture(dmiptex);
815 pixels = freepixels = W_GetTexture(tx->name);
818 tx->width = image_width;
819 tx->height = image_height;
820 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);
821 if (Image_CheckAlpha(pixels, image_width * image_height, true))
823 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
824 for (j = 0;j < image_width * image_height * 4;j += 4)
826 fogpixels[j + 0] = 255;
827 fogpixels[j + 1] = 255;
828 fogpixels[j + 2] = 255;
829 fogpixels[j + 3] = pixels[j + 3];
831 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
836 Mem_Free(freepixels);
838 else if (mtdata) // texture included
839 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
842 if (tx->skin.base == NULL)
847 tx->skin.base = r_notexture;
850 if (tx->name[0] == '*')
852 // turb does not block movement
853 tx->flags &= ~SURF_SOLIDCLIP;
854 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
855 // LordHavoc: some turbulent textures should be fullbright and solid
856 if (!strncmp(tx->name,"*lava",5)
857 || !strncmp(tx->name,"*teleport",9)
858 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
859 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
861 tx->flags |= SURF_WATERALPHA;
862 tx->shader = &Cshader_water;
864 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
866 tx->flags |= SURF_DRAWSKY;
867 tx->shader = &Cshader_sky;
871 tx->flags |= SURF_LIGHTMAP;
873 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
874 tx->shader = &Cshader_wall_lightmap;
877 // start out with no animation
878 tx->currentframe = tx;
881 // sequence the animations
882 for (i = 0;i < m->nummiptex;i++)
884 tx = loadmodel->brushq1.textures + i;
885 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
887 if (tx->anim_total[0] || tx->anim_total[1])
888 continue; // already sequenced
890 // find the number of frames in the animation
891 memset(anims, 0, sizeof(anims));
892 memset(altanims, 0, sizeof(altanims));
894 for (j = i;j < m->nummiptex;j++)
896 tx2 = loadmodel->brushq1.textures + j;
897 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
901 if (num >= '0' && num <= '9')
902 anims[num - '0'] = tx2;
903 else if (num >= 'a' && num <= 'j')
904 altanims[num - 'a'] = tx2;
906 Con_Printf("Bad animating texture %s\n", tx->name);
910 for (j = 0;j < 10;j++)
917 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
920 for (j = 0;j < max;j++)
924 Con_Printf("Missing frame %i of %s\n", j, tx->name);
928 for (j = 0;j < altmax;j++)
932 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
941 // if there is no alternate animation, duplicate the primary
942 // animation into the alternate
944 for (k = 0;k < 10;k++)
945 altanims[k] = anims[k];
948 // link together the primary animation
949 for (j = 0;j < max;j++)
952 tx2->animated = true;
953 tx2->anim_total[0] = max;
954 tx2->anim_total[1] = altmax;
955 for (k = 0;k < 10;k++)
957 tx2->anim_frames[0][k] = anims[k];
958 tx2->anim_frames[1][k] = altanims[k];
962 // if there really is an alternate anim...
963 if (anims[0] != altanims[0])
965 // link together the alternate animation
966 for (j = 0;j < altmax;j++)
969 tx2->animated = true;
970 // the primary/alternate are reversed here
971 tx2->anim_total[0] = altmax;
972 tx2->anim_total[1] = max;
973 for (k = 0;k < 10;k++)
975 tx2->anim_frames[0][k] = altanims[k];
976 tx2->anim_frames[1][k] = anims[k];
983 static void Mod_Q1BSP_LoadLighting(lump_t *l)
986 qbyte *in, *out, *data, d;
987 char litfilename[1024];
988 loadmodel->brushq1.lightdata = NULL;
989 if (loadmodel->brushq1.ishlbsp) // LordHavoc: load the colored lighting data straight
991 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
992 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
994 else // LordHavoc: bsp version 29 (normal white lighting)
996 // LordHavoc: hope is not lost yet, check for a .lit file to load
997 strcpy(litfilename, loadmodel->name);
998 FS_StripExtension(litfilename, litfilename);
999 strcat(litfilename, ".lit");
1000 data = (qbyte*) FS_LoadFile(litfilename, false);
1003 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1005 i = LittleLong(((int *)data)[1]);
1008 Con_DPrintf("loaded %s\n", litfilename);
1009 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1010 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1016 Con_Printf("Unknown .lit file version (%d)\n", i);
1022 if (fs_filesize == 8)
1023 Con_Printf("Empty .lit file, ignoring\n");
1025 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1029 // LordHavoc: oh well, expand the white lighting data
1032 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1033 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1034 out = loadmodel->brushq1.lightdata;
1035 memcpy(in, mod_base + l->fileofs, l->filelen);
1036 for (i = 0;i < l->filelen;i++)
1046 static void Mod_Q1BSP_LoadLightList(void)
1048 int a, n, numlights;
1049 char lightsfilename[1024], *s, *t, *lightsstring;
1052 strcpy(lightsfilename, loadmodel->name);
1053 FS_StripExtension(lightsfilename, lightsfilename);
1054 strcat(lightsfilename, ".lights");
1055 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1061 while (*s && *s != '\n')
1065 Mem_Free(lightsstring);
1066 Host_Error("lights file must end with a newline\n");
1071 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1074 while (*s && n < numlights)
1077 while (*s && *s != '\n')
1081 Mem_Free(lightsstring);
1082 Host_Error("misparsed lights file!\n");
1084 e = loadmodel->brushq1.lights + n;
1086 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);
1090 Mem_Free(lightsstring);
1091 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);
1098 Mem_Free(lightsstring);
1099 Host_Error("misparsed lights file!\n");
1101 loadmodel->brushq1.numlights = numlights;
1102 Mem_Free(lightsstring);
1107 static int castshadowcount = 0;
1108 static void Mod_Q1BSP_ProcessLightList(void)
1110 int j, k, l, *mark, lnum;
1118 for (lnum = 0, e = loadmodel->brushq1.lights;lnum < loadmodel->brushq1.numlights;lnum++, e++)
1120 e->cullradius2 = DotProduct(e->light, e->light) / (e->falloff * e->falloff * 8192.0f * 8192.0f * 2.0f * 2.0f);// + 4096.0f;
1121 if (e->cullradius2 > 4096.0f * 4096.0f)
1122 e->cullradius2 = 4096.0f * 4096.0f;
1123 e->cullradius = e->lightradius = sqrt(e->cullradius2);
1124 leaf = Mod_Q1BSP_PointInLeaf(e->origin, loadmodel);
1125 if (leaf->compressed_vis)
1126 pvs = Mod_Q1BSP_DecompressVis(leaf->compressed_vis, loadmodel);
1128 pvs = mod_q1bsp_novis;
1129 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
1130 loadmodel->brushq1.surfacevisframes[j] = -1;
1131 for (j = 0, leaf = loadmodel->brushq1.leafs + 1;j < loadmodel->brushq1.numleafs - 1;j++, leaf++)
1133 if (pvs[j >> 3] & (1 << (j & 7)))
1135 for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++)
1137 surf = loadmodel->brushq1.surfaces + *mark;
1138 if (surf->number != *mark)
1139 Con_Printf("%d != %d\n", surf->number, *mark);
1140 dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1141 if (surf->flags & SURF_PLANEBACK)
1143 if (dist > 0 && dist < e->cullradius)
1145 temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
1146 temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
1147 temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
1148 if (DotProduct(temp, temp) < lightradius2)
1149 loadmodel->brushq1.surfacevisframes[*mark] = -2;
1154 // build list of light receiving surfaces
1156 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
1157 if (loadmodel->brushq1.surfacevisframes[j] == -2)
1160 if (e->numsurfaces > 0)
1162 e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
1164 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
1165 if (loadmodel->brushq1.surfacevisframes[j] == -2)
1166 e->surfaces[e->numsurfaces++] = loadmodel->brushq1.surfaces + j;
1168 // find bounding box and sphere of lit surfaces
1169 // (these will be used for creating a shape to clip the light)
1171 for (j = 0;j < e->numsurfaces;j++)
1173 surf = e->surfaces[j];
1176 VectorCopy(surf->poly_verts, e->mins);
1177 VectorCopy(surf->poly_verts, e->maxs);
1179 for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
1181 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
1182 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
1183 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
1184 VectorSubtract(v, e->origin, temp);
1185 dist = DotProduct(temp, temp);
1190 if (e->cullradius2 > radius2)
1192 e->cullradius2 = radius2;
1193 e->cullradius = sqrt(e->cullradius2);
1195 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
1196 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
1197 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
1198 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
1199 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
1200 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
1201 // clip shadow volumes against eachother to remove unnecessary
1202 // polygons(and sections of polygons)
1204 //vec3_t polymins, polymaxs;
1206 float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1207 float f, *v0, *v1, projectdistance;
1209 e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 1024);
1212 vec3_t outermins, outermaxs, innermins, innermaxs;
1213 innermins[0] = e->mins[0] - 1;
1214 innermins[1] = e->mins[1] - 1;
1215 innermins[2] = e->mins[2] - 1;
1216 innermaxs[0] = e->maxs[0] + 1;
1217 innermaxs[1] = e->maxs[1] + 1;
1218 innermaxs[2] = e->maxs[2] + 1;
1219 outermins[0] = loadmodel->normalmins[0] - 1;
1220 outermins[1] = loadmodel->normalmins[1] - 1;
1221 outermins[2] = loadmodel->normalmins[2] - 1;
1222 outermaxs[0] = loadmodel->normalmaxs[0] + 1;
1223 outermaxs[1] = loadmodel->normalmaxs[1] + 1;
1224 outermaxs[2] = loadmodel->normalmaxs[2] + 1;
1225 // add bounding box around the whole shadow volume set,
1226 // facing inward to limit light area, with an outer bounding box
1227 // facing outward (this is needed by the shadow rendering method)
1229 verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1230 verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1231 verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1232 verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1233 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1234 verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1235 verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1236 verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1237 verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1238 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1240 verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1241 verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1242 verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1243 verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1244 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1245 verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1246 verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1247 verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1248 verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1249 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1251 verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1252 verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1253 verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1254 verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1255 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1256 verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1257 verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1258 verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1259 verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1260 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1262 verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1263 verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1264 verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1265 verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1266 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1267 verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1268 verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1269 verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1270 verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1271 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1273 verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1274 verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
1275 verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
1276 verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1277 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1278 verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1279 verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
1280 verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
1281 verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1282 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1284 verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
1285 verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1286 verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1287 verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
1288 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1289 verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
1290 verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1291 verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1292 verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
1293 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1297 for (j = 0;j < e->numsurfaces;j++)
1299 surf = e->surfaces[j];
1300 if (surf->flags & SURF_SHADOWCAST)
1301 surf->castshadow = castshadowcount;
1303 for (j = 0;j < e->numsurfaces;j++)
1305 surf = e->surfaces[j];
1306 if (surf->castshadow != castshadowcount)
1308 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1309 if (surf->flags & SURF_PLANEBACK)
1311 projectdistance = e->lightradius;
1312 if (maxverts < surf->poly_numverts)
1314 maxverts = surf->poly_numverts;
1317 verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1319 // copy the original polygon, for the front cap of the volume
1320 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1322 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1323 // project the original polygon, reversed, for the back cap of the volume
1324 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1326 VectorSubtract(v0, e->origin, temp);
1327 VectorNormalize(temp);
1328 VectorMA(v0, projectdistance, temp, v1);
1330 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1331 // project the shadow volume sides
1332 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)
1334 if (!surf->neighborsurfaces[l] || surf->neighborsurfaces[l]->castshadow != castshadowcount)
1336 VectorCopy(v1, &verts[0]);
1337 VectorCopy(v0, &verts[3]);
1338 VectorCopy(v0, &verts[6]);
1339 VectorCopy(v1, &verts[9]);
1340 VectorSubtract(&verts[6], e->origin, temp);
1341 VectorNormalize(temp);
1342 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1343 VectorSubtract(&verts[9], e->origin, temp);
1344 VectorNormalize(temp);
1345 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1346 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1350 // build the triangle mesh
1351 e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
1355 for (mesh = e->shadowvolume;mesh;mesh = mesh->next)
1356 l += mesh->numtriangles;
1357 Con_Printf("light %i shadow volume built containing %i triangles\n", lnum, l);
1365 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1367 loadmodel->brushq1.visdata = NULL;
1370 loadmodel->brushq1.visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1371 memcpy(loadmodel->brushq1.visdata, mod_base + l->fileofs, l->filelen);
1374 // used only for HalfLife maps
1375 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1377 char key[128], value[4096];
1382 if (!COM_ParseToken(&data, false))
1384 if (com_token[0] != '{')
1388 if (!COM_ParseToken(&data, false))
1390 if (com_token[0] == '}')
1391 break; // end of worldspawn
1392 if (com_token[0] == '_')
1393 strcpy(key, com_token + 1);
1395 strcpy(key, com_token);
1396 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1397 key[strlen(key)-1] = 0;
1398 if (!COM_ParseToken(&data, false))
1400 strcpy(value, com_token);
1401 if (!strcmp("wad", key)) // for HalfLife maps
1403 if (loadmodel->brushq1.ishlbsp)
1406 for (i = 0;i < 4096;i++)
1407 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1413 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1414 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1416 else if (value[i] == ';' || value[i] == 0)
1420 strcpy(wadname, "textures/");
1421 strcat(wadname, &value[j]);
1422 W_LoadTextureWadFile(wadname, false);
1434 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1436 loadmodel->brush.entities = NULL;
1439 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1440 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1441 if (loadmodel->brushq1.ishlbsp)
1442 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1446 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1452 in = (void *)(mod_base + l->fileofs);
1453 if (l->filelen % sizeof(*in))
1454 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1455 count = l->filelen / sizeof(*in);
1456 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1458 loadmodel->brushq1.vertexes = out;
1459 loadmodel->brushq1.numvertexes = count;
1461 for ( i=0 ; i<count ; i++, in++, out++)
1463 out->position[0] = LittleFloat(in->point[0]);
1464 out->position[1] = LittleFloat(in->point[1]);
1465 out->position[2] = LittleFloat(in->point[2]);
1469 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1475 in = (void *)(mod_base + l->fileofs);
1476 if (l->filelen % sizeof(*in))
1477 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1478 count = l->filelen / sizeof(*in);
1479 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1481 loadmodel->brushq1.submodels = out;
1482 loadmodel->brushq1.numsubmodels = count;
1484 for ( i=0 ; i<count ; i++, in++, out++)
1486 for (j=0 ; j<3 ; j++)
1488 // spread the mins / maxs by a pixel
1489 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1490 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1491 out->origin[j] = LittleFloat(in->origin[j]);
1493 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1494 out->headnode[j] = LittleLong(in->headnode[j]);
1495 out->visleafs = LittleLong(in->visleafs);
1496 out->firstface = LittleLong(in->firstface);
1497 out->numfaces = LittleLong(in->numfaces);
1501 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1507 in = (void *)(mod_base + l->fileofs);
1508 if (l->filelen % sizeof(*in))
1509 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1510 count = l->filelen / sizeof(*in);
1511 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1513 loadmodel->brushq1.edges = out;
1514 loadmodel->brushq1.numedges = count;
1516 for ( i=0 ; i<count ; i++, in++, out++)
1518 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1519 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1523 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1527 int i, j, k, count, miptex;
1529 in = (void *)(mod_base + l->fileofs);
1530 if (l->filelen % sizeof(*in))
1531 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1532 count = l->filelen / sizeof(*in);
1533 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1535 loadmodel->brushq1.texinfo = out;
1536 loadmodel->brushq1.numtexinfo = count;
1538 for (i = 0;i < count;i++, in++, out++)
1540 for (k = 0;k < 2;k++)
1541 for (j = 0;j < 4;j++)
1542 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1544 miptex = LittleLong(in->miptex);
1545 out->flags = LittleLong(in->flags);
1547 out->texture = NULL;
1548 if (loadmodel->brushq1.textures)
1550 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1551 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1553 out->texture = loadmodel->brushq1.textures + miptex;
1555 if (out->flags & TEX_SPECIAL)
1557 // if texture chosen is NULL or the shader needs a lightmap,
1558 // force to notexture water shader
1559 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1560 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1564 // if texture chosen is NULL, force to notexture
1565 if (out->texture == NULL)
1566 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1572 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1577 mins[0] = mins[1] = mins[2] = 9999;
1578 maxs[0] = maxs[1] = maxs[2] = -9999;
1580 for (i = 0;i < numverts;i++)
1582 for (j = 0;j < 3;j++, v++)
1592 #define MAX_SUBDIVPOLYTRIANGLES 4096
1593 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1595 static int subdivpolyverts, subdivpolytriangles;
1596 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1597 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1599 static int subdivpolylookupvert(vec3_t v)
1602 for (i = 0;i < subdivpolyverts;i++)
1603 if (subdivpolyvert[i][0] == v[0]
1604 && subdivpolyvert[i][1] == v[1]
1605 && subdivpolyvert[i][2] == v[2])
1607 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1608 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1609 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1610 return subdivpolyverts++;
1613 static void SubdividePolygon(int numverts, float *verts)
1615 int i, i1, i2, i3, f, b, c, p;
1616 vec3_t mins, maxs, front[256], back[256];
1617 float m, *pv, *cv, dist[256], frac;
1620 Host_Error("SubdividePolygon: ran out of verts in buffer");
1622 BoundPoly(numverts, verts, mins, maxs);
1624 for (i = 0;i < 3;i++)
1626 m = (mins[i] + maxs[i]) * 0.5;
1627 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1628 if (maxs[i] - m < 8)
1630 if (m - mins[i] < 8)
1634 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1635 dist[c] = cv[i] - m;
1638 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1642 VectorCopy(pv, front[f]);
1647 VectorCopy(pv, back[b]);
1650 if (dist[p] == 0 || dist[c] == 0)
1652 if ((dist[p] > 0) != (dist[c] > 0) )
1655 frac = dist[p] / (dist[p] - dist[c]);
1656 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1657 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1658 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1664 SubdividePolygon(f, front[0]);
1665 SubdividePolygon(b, back[0]);
1669 i1 = subdivpolylookupvert(verts);
1670 i2 = subdivpolylookupvert(verts + 3);
1671 for (i = 2;i < numverts;i++)
1673 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1675 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1679 i3 = subdivpolylookupvert(verts + i * 3);
1680 subdivpolyindex[subdivpolytriangles][0] = i1;
1681 subdivpolyindex[subdivpolytriangles][1] = i2;
1682 subdivpolyindex[subdivpolytriangles][2] = i3;
1684 subdivpolytriangles++;
1688 //Breaks a polygon up along axial 64 unit
1689 //boundaries so that turbulent and sky warps
1690 //can be done reasonably.
1691 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1697 subdivpolytriangles = 0;
1698 subdivpolyverts = 0;
1699 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1700 if (subdivpolytriangles < 1)
1701 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1703 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1704 mesh->numverts = subdivpolyverts;
1705 mesh->numtriangles = subdivpolytriangles;
1706 mesh->vertex = (surfvertex_t *)(mesh + 1);
1707 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1708 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1710 for (i = 0;i < mesh->numtriangles;i++)
1711 for (j = 0;j < 3;j++)
1712 mesh->index[i*3+j] = subdivpolyindex[i][j];
1714 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1716 VectorCopy(subdivpolyvert[i], v->v);
1717 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1718 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1723 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1726 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1727 mesh->numverts = numverts;
1728 mesh->numtriangles = numtriangles;
1729 mesh->vertex3f = (float *)(mesh + 1);
1730 mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1731 mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1732 mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1733 mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1734 mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1735 mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1736 mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1737 mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1738 mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1742 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1745 float *vec, *vert, mins[3], maxs[3], val, *v;
1748 // convert edges back to a normal polygon
1749 surf->poly_numverts = numedges;
1750 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1751 for (i = 0;i < numedges;i++)
1753 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1755 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1757 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1758 VectorCopy(vec, vert);
1762 // calculate polygon bounding box and center
1763 vert = surf->poly_verts;
1764 VectorCopy(vert, mins);
1765 VectorCopy(vert, maxs);
1767 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1769 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1770 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1771 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1773 VectorCopy(mins, surf->poly_mins);
1774 VectorCopy(maxs, surf->poly_maxs);
1775 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1776 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1777 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1779 // generate surface extents information
1780 tex = surf->texinfo;
1781 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1782 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1783 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1785 for (j = 0;j < 2;j++)
1787 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1794 for (i = 0;i < 2;i++)
1796 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1797 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1801 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1805 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1809 in = (void *)(mod_base + l->fileofs);
1810 if (l->filelen % sizeof(*in))
1811 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1812 count = l->filelen / sizeof(*in);
1813 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1815 loadmodel->brushq1.numsurfaces = count;
1816 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1817 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1818 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1820 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++)
1822 surf->number = surfnum;
1823 // FIXME: validate edges, texinfo, etc?
1824 firstedge = LittleLong(in->firstedge);
1825 numedges = LittleShort(in->numedges);
1826 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)
1827 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1828 i = LittleShort(in->texinfo);
1829 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1830 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1831 surf->texinfo = loadmodel->brushq1.texinfo + i;
1832 surf->flags = surf->texinfo->texture->flags;
1834 planenum = LittleShort(in->planenum);
1835 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1836 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1838 if (LittleShort(in->side))
1839 surf->flags |= SURF_PLANEBACK;
1841 surf->plane = loadmodel->brushq1.planes + planenum;
1843 // clear lightmap (filled in later)
1844 surf->lightmaptexture = NULL;
1846 // force lightmap upload on first time seeing the surface
1847 surf->cached_dlight = true;
1849 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1851 ssize = (surf->extents[0] >> 4) + 1;
1852 tsize = (surf->extents[1] >> 4) + 1;
1855 for (i = 0;i < MAXLIGHTMAPS;i++)
1856 surf->styles[i] = in->styles[i];
1857 i = LittleLong(in->lightofs);
1859 surf->samples = NULL;
1860 else if (loadmodel->brushq1.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1861 surf->samples = loadmodel->brushq1.lightdata + i;
1862 else // LordHavoc: white lighting (bsp version 29)
1863 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1865 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1867 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1868 Host_Error("Bad surface extents");
1869 // stainmap for permanent marks on walls
1870 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1872 memset(surf->stainsamples, 255, ssize * tsize * 3);
1876 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1877 loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1879 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++)
1881 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1882 mesh->numverts = surf->poly_numverts;
1883 mesh->numtriangles = surf->poly_numverts - 2;
1884 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1885 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1886 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1887 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1888 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1889 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1890 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1891 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1892 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1893 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1895 surf->lightmaptexturestride = 0;
1896 surf->lightmaptexture = NULL;
1898 for (i = 0;i < mesh->numverts;i++)
1900 mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1901 mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1902 mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1903 s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1904 t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1905 mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1906 mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1907 mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1908 mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1909 mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1910 mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1911 mesh->lightmapoffsets[i] = 0;
1914 for (i = 0;i < mesh->numtriangles;i++)
1916 mesh->element3i[i * 3 + 0] = 0;
1917 mesh->element3i[i * 3 + 1] = i + 1;
1918 mesh->element3i[i * 3 + 2] = i + 2;
1921 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1922 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1924 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1926 int i, iu, iv, smax, tmax;
1927 float u, v, ubase, vbase, uscale, vscale;
1929 smax = surf->extents[0] >> 4;
1930 tmax = surf->extents[1] >> 4;
1932 surf->flags |= SURF_LIGHTMAP;
1933 if (r_miplightmaps.integer)
1935 surf->lightmaptexturestride = smax+1;
1936 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);
1940 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1941 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);
1943 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1944 uscale = (uscale - ubase) / (smax + 1);
1945 vscale = (vscale - vbase) / (tmax + 1);
1947 for (i = 0;i < mesh->numverts;i++)
1949 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1950 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1951 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1952 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1953 // LordHavoc: calc lightmap data offset for vertex lighting to use
1956 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1962 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1964 node->parent = parent;
1965 if (node->contents < 0)
1967 Mod_Q1BSP_SetParent(node->children[0], node);
1968 Mod_Q1BSP_SetParent(node->children[1], node);
1971 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1977 in = (void *)(mod_base + l->fileofs);
1978 if (l->filelen % sizeof(*in))
1979 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1980 count = l->filelen / sizeof(*in);
1981 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1983 loadmodel->brushq1.nodes = out;
1984 loadmodel->brushq1.numnodes = count;
1986 for ( i=0 ; i<count ; i++, in++, out++)
1988 for (j=0 ; j<3 ; j++)
1990 out->mins[j] = LittleShort(in->mins[j]);
1991 out->maxs[j] = LittleShort(in->maxs[j]);
1994 p = LittleLong(in->planenum);
1995 out->plane = loadmodel->brushq1.planes + p;
1997 out->firstsurface = LittleShort(in->firstface);
1998 out->numsurfaces = LittleShort(in->numfaces);
2000 for (j=0 ; j<2 ; j++)
2002 p = LittleShort(in->children[j]);
2004 out->children[j] = loadmodel->brushq1.nodes + p;
2006 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
2010 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
2013 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
2019 in = (void *)(mod_base + l->fileofs);
2020 if (l->filelen % sizeof(*in))
2021 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2022 count = l->filelen / sizeof(*in);
2023 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2025 loadmodel->brushq1.leafs = out;
2026 loadmodel->brushq1.numleafs = count;
2028 for ( i=0 ; i<count ; i++, in++, out++)
2030 for (j=0 ; j<3 ; j++)
2032 out->mins[j] = LittleShort(in->mins[j]);
2033 out->maxs[j] = LittleShort(in->maxs[j]);
2036 p = LittleLong(in->contents);
2039 out->firstmarksurface = loadmodel->brushq1.marksurfaces +
2040 LittleShort(in->firstmarksurface);
2041 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
2043 p = LittleLong(in->visofs);
2045 out->compressed_vis = NULL;
2047 out->compressed_vis = loadmodel->brushq1.visdata + p;
2049 for (j=0 ; j<4 ; j++)
2050 out->ambient_sound_level[j] = in->ambient_level[j];
2052 // FIXME: Insert caustics here
2056 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
2058 dclipnode_t *in, *out;
2062 in = (void *)(mod_base + l->fileofs);
2063 if (l->filelen % sizeof(*in))
2064 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2065 count = l->filelen / sizeof(*in);
2066 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2068 loadmodel->brushq1.clipnodes = out;
2069 loadmodel->brushq1.numclipnodes = count;
2071 if (loadmodel->brushq1.ishlbsp)
2073 hull = &loadmodel->brushq1.hulls[1];
2074 hull->clipnodes = out;
2075 hull->firstclipnode = 0;
2076 hull->lastclipnode = count-1;
2077 hull->planes = loadmodel->brushq1.planes;
2078 hull->clip_mins[0] = -16;
2079 hull->clip_mins[1] = -16;
2080 hull->clip_mins[2] = -36;
2081 hull->clip_maxs[0] = 16;
2082 hull->clip_maxs[1] = 16;
2083 hull->clip_maxs[2] = 36;
2084 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2086 hull = &loadmodel->brushq1.hulls[2];
2087 hull->clipnodes = out;
2088 hull->firstclipnode = 0;
2089 hull->lastclipnode = count-1;
2090 hull->planes = loadmodel->brushq1.planes;
2091 hull->clip_mins[0] = -32;
2092 hull->clip_mins[1] = -32;
2093 hull->clip_mins[2] = -32;
2094 hull->clip_maxs[0] = 32;
2095 hull->clip_maxs[1] = 32;
2096 hull->clip_maxs[2] = 32;
2097 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2099 hull = &loadmodel->brushq1.hulls[3];
2100 hull->clipnodes = out;
2101 hull->firstclipnode = 0;
2102 hull->lastclipnode = count-1;
2103 hull->planes = loadmodel->brushq1.planes;
2104 hull->clip_mins[0] = -16;
2105 hull->clip_mins[1] = -16;
2106 hull->clip_mins[2] = -18;
2107 hull->clip_maxs[0] = 16;
2108 hull->clip_maxs[1] = 16;
2109 hull->clip_maxs[2] = 18;
2110 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2114 hull = &loadmodel->brushq1.hulls[1];
2115 hull->clipnodes = out;
2116 hull->firstclipnode = 0;
2117 hull->lastclipnode = count-1;
2118 hull->planes = loadmodel->brushq1.planes;
2119 hull->clip_mins[0] = -16;
2120 hull->clip_mins[1] = -16;
2121 hull->clip_mins[2] = -24;
2122 hull->clip_maxs[0] = 16;
2123 hull->clip_maxs[1] = 16;
2124 hull->clip_maxs[2] = 32;
2125 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2127 hull = &loadmodel->brushq1.hulls[2];
2128 hull->clipnodes = out;
2129 hull->firstclipnode = 0;
2130 hull->lastclipnode = count-1;
2131 hull->planes = loadmodel->brushq1.planes;
2132 hull->clip_mins[0] = -32;
2133 hull->clip_mins[1] = -32;
2134 hull->clip_mins[2] = -24;
2135 hull->clip_maxs[0] = 32;
2136 hull->clip_maxs[1] = 32;
2137 hull->clip_maxs[2] = 64;
2138 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2141 for (i=0 ; i<count ; i++, out++, in++)
2143 out->planenum = LittleLong(in->planenum);
2144 out->children[0] = LittleShort(in->children[0]);
2145 out->children[1] = LittleShort(in->children[1]);
2146 if (out->children[0] >= count || out->children[1] >= count)
2147 Host_Error("Corrupt clipping hull(out of range child)\n");
2151 //Duplicate the drawing hull structure as a clipping hull
2152 static void Mod_Q1BSP_MakeHull0(void)
2159 hull = &loadmodel->brushq1.hulls[0];
2161 in = loadmodel->brushq1.nodes;
2162 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
2164 hull->clipnodes = out;
2165 hull->firstclipnode = 0;
2166 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
2167 hull->planes = loadmodel->brushq1.planes;
2169 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
2171 out->planenum = in->plane - loadmodel->brushq1.planes;
2172 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
2173 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
2177 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
2182 in = (void *)(mod_base + l->fileofs);
2183 if (l->filelen % sizeof(*in))
2184 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2185 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2186 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2188 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2190 j = (unsigned) LittleShort(in[i]);
2191 if (j >= loadmodel->brushq1.numsurfaces)
2192 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2193 loadmodel->brushq1.marksurfaces[i] = j;
2197 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2202 in = (void *)(mod_base + l->fileofs);
2203 if (l->filelen % sizeof(*in))
2204 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2205 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2206 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2208 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2209 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2213 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2219 in = (void *)(mod_base + l->fileofs);
2220 if (l->filelen % sizeof(*in))
2221 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2223 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2224 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2226 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2228 out->normal[0] = LittleFloat(in->normal[0]);
2229 out->normal[1] = LittleFloat(in->normal[1]);
2230 out->normal[2] = LittleFloat(in->normal[2]);
2231 out->dist = LittleFloat(in->dist);
2237 #define MAX_POINTS_ON_WINDING 64
2243 double points[8][3]; // variable sized
2252 static winding_t *NewWinding(int points)
2257 if (points > MAX_POINTS_ON_WINDING)
2258 Sys_Error("NewWinding: too many points\n");
2260 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
2261 w = Mem_Alloc(loadmodel->mempool, size);
2267 static void FreeWinding(winding_t *w)
2277 static winding_t *BaseWindingForPlane(mplane_t *p)
2279 double org[3], vright[3], vup[3], normal[3];
2282 VectorCopy(p->normal, normal);
2283 VectorVectorsDouble(normal, vright, vup);
2285 VectorScale(vup, 1024.0*1024.0*1024.0, vup);
2286 VectorScale(vright, 1024.0*1024.0*1024.0, vright);
2288 // project a really big axis aligned box onto the plane
2291 VectorScale(p->normal, p->dist, org);
2293 VectorSubtract(org, vright, w->points[0]);
2294 VectorAdd(w->points[0], vup, w->points[0]);
2296 VectorAdd(org, vright, w->points[1]);
2297 VectorAdd(w->points[1], vup, w->points[1]);
2299 VectorAdd(org, vright, w->points[2]);
2300 VectorSubtract(w->points[2], vup, w->points[2]);
2302 VectorSubtract(org, vright, w->points[3]);
2303 VectorSubtract(w->points[3], vup, w->points[3]);
2314 Clips the winding to the plane, returning the new winding on the positive side
2315 Frees the input winding.
2316 If keepon is true, an exactly on-plane winding will be saved, otherwise
2317 it will be clipped away.
2320 static winding_t *ClipWinding(winding_t *in, mplane_t *split, int keepon)
2322 double dists[MAX_POINTS_ON_WINDING + 1];
2323 int sides[MAX_POINTS_ON_WINDING + 1];
2332 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2334 // determine sides for each point
2335 for (i = 0;i < in->numpoints;i++)
2337 dists[i] = dot = DotProduct(in->points[i], split->normal) - split->dist;
2338 if (dot > ON_EPSILON)
2339 sides[i] = SIDE_FRONT;
2340 else if (dot < -ON_EPSILON)
2341 sides[i] = SIDE_BACK;
2346 sides[i] = sides[0];
2347 dists[i] = dists[0];
2349 if (keepon && !counts[0] && !counts[1])
2360 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2361 if (maxpts > MAX_POINTS_ON_WINDING)
2362 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2364 neww = NewWinding(maxpts);
2366 for (i = 0;i < in->numpoints;i++)
2368 if (neww->numpoints >= maxpts)
2369 Sys_Error("ClipWinding: points exceeded estimate");
2373 if (sides[i] == SIDE_ON)
2375 VectorCopy(p1, neww->points[neww->numpoints]);
2380 if (sides[i] == SIDE_FRONT)
2382 VectorCopy(p1, neww->points[neww->numpoints]);
2386 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2389 // generate a split point
2390 p2 = in->points[(i+1)%in->numpoints];
2392 dot = dists[i] / (dists[i]-dists[i+1]);
2393 for (j = 0;j < 3;j++)
2394 { // avoid round off error when possible
2395 if (split->normal[j] == 1)
2396 mid[j] = split->dist;
2397 else if (split->normal[j] == -1)
2398 mid[j] = -split->dist;
2400 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2403 VectorCopy(mid, neww->points[neww->numpoints]);
2407 // free the original winding
2418 Divides a winding by a plane, producing one or two windings. The
2419 original winding is not damaged or freed. If only on one side, the
2420 returned winding will be the input winding. If on both sides, two
2421 new windings will be created.
2424 static void DivideWinding(winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2426 double dists[MAX_POINTS_ON_WINDING + 1];
2427 int sides[MAX_POINTS_ON_WINDING + 1];
2436 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2438 // determine sides for each point
2439 for (i = 0;i < in->numpoints;i++)
2441 dot = DotProduct(in->points[i], split->normal);
2444 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2445 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2446 else sides[i] = SIDE_ON;
2449 sides[i] = sides[0];
2450 dists[i] = dists[0];
2452 *front = *back = NULL;
2465 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2467 if (maxpts > MAX_POINTS_ON_WINDING)
2468 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2470 *front = f = NewWinding(maxpts);
2471 *back = b = NewWinding(maxpts);
2473 for (i = 0;i < in->numpoints;i++)
2475 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2476 Sys_Error("DivideWinding: points exceeded estimate");
2480 if (sides[i] == SIDE_ON)
2482 VectorCopy(p1, f->points[f->numpoints]);
2484 VectorCopy(p1, b->points[b->numpoints]);
2489 if (sides[i] == SIDE_FRONT)
2491 VectorCopy(p1, f->points[f->numpoints]);
2494 else if (sides[i] == SIDE_BACK)
2496 VectorCopy(p1, b->points[b->numpoints]);
2500 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2503 // generate a split point
2504 p2 = in->points[(i+1)%in->numpoints];
2506 dot = dists[i] / (dists[i]-dists[i+1]);
2507 for (j = 0;j < 3;j++)
2508 { // avoid round off error when possible
2509 if (split->normal[j] == 1)
2510 mid[j] = split->dist;
2511 else if (split->normal[j] == -1)
2512 mid[j] = -split->dist;
2514 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2517 VectorCopy(mid, f->points[f->numpoints]);
2519 VectorCopy(mid, b->points[b->numpoints]);
2524 typedef struct portal_s
2527 mnode_t *nodes[2]; // [0] = front side of plane
2528 struct portal_s *next[2];
2530 struct portal_s *chain; // all portals are linked into a list
2534 static portal_t *portalchain;
2541 static portal_t *AllocPortal(void)
2544 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2545 p->chain = portalchain;
2550 static void FreePortal(portal_t *p)
2555 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2557 // calculate children first
2558 if (node->children[0]->contents >= 0)
2559 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2560 if (node->children[1]->contents >= 0)
2561 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2563 // make combined bounding box from children
2564 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2565 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2566 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2567 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2568 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2569 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2572 static void Mod_Q1BSP_FinalizePortals(void)
2574 int i, j, numportals, numpoints;
2575 portal_t *p, *pnext;
2578 mleaf_t *leaf, *endleaf;
2581 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2582 leaf = loadmodel->brushq1.leafs;
2583 endleaf = leaf + loadmodel->brushq1.numleafs;
2584 for (;leaf < endleaf;leaf++)
2586 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2587 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2594 for (i = 0;i < 2;i++)
2596 leaf = (mleaf_t *)p->nodes[i];
2598 for (j = 0;j < w->numpoints;j++)
2600 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2601 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2602 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2603 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2604 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2605 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2612 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2614 // tally up portal and point counts
2620 // note: this check must match the one below or it will usually corrupt memory
2621 // 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
2622 if (p->winding && p->nodes[0] != p->nodes[1]
2623 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2624 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2627 numpoints += p->winding->numpoints * 2;
2631 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2632 loadmodel->brushq1.numportals = numportals;
2633 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2634 loadmodel->brushq1.numportalpoints = numpoints;
2635 // clear all leaf portal chains
2636 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2637 loadmodel->brushq1.leafs[i].portals = NULL;
2638 // process all portals in the global portal chain, while freeing them
2639 portal = loadmodel->brushq1.portals;
2640 point = loadmodel->brushq1.portalpoints;
2649 // note: this check must match the one above or it will usually corrupt memory
2650 // 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
2651 if (p->nodes[0] != p->nodes[1]
2652 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2653 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2655 // first make the back to front portal(forward portal)
2656 portal->points = point;
2657 portal->numpoints = p->winding->numpoints;
2658 portal->plane.dist = p->plane.dist;
2659 VectorCopy(p->plane.normal, portal->plane.normal);
2660 portal->here = (mleaf_t *)p->nodes[1];
2661 portal->past = (mleaf_t *)p->nodes[0];
2663 for (j = 0;j < portal->numpoints;j++)
2665 VectorCopy(p->winding->points[j], point->position);
2668 PlaneClassify(&portal->plane);
2670 // link into leaf's portal chain
2671 portal->next = portal->here->portals;
2672 portal->here->portals = portal;
2674 // advance to next portal
2677 // then make the front to back portal(backward portal)
2678 portal->points = point;
2679 portal->numpoints = p->winding->numpoints;
2680 portal->plane.dist = -p->plane.dist;
2681 VectorNegate(p->plane.normal, portal->plane.normal);
2682 portal->here = (mleaf_t *)p->nodes[0];
2683 portal->past = (mleaf_t *)p->nodes[1];
2685 for (j = portal->numpoints - 1;j >= 0;j--)
2687 VectorCopy(p->winding->points[j], point->position);
2690 PlaneClassify(&portal->plane);
2692 // link into leaf's portal chain
2693 portal->next = portal->here->portals;
2694 portal->here->portals = portal;
2696 // advance to next portal
2699 FreeWinding(p->winding);
2711 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2714 Host_Error("AddPortalToNodes: NULL front node");
2716 Host_Error("AddPortalToNodes: NULL back node");
2717 if (p->nodes[0] || p->nodes[1])
2718 Host_Error("AddPortalToNodes: already included");
2719 // 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
2721 p->nodes[0] = front;
2722 p->next[0] = (portal_t *)front->portals;
2723 front->portals = (mportal_t *)p;
2726 p->next[1] = (portal_t *)back->portals;
2727 back->portals = (mportal_t *)p;
2732 RemovePortalFromNode
2735 static void RemovePortalFromNodes(portal_t *portal)
2739 void **portalpointer;
2741 for (i = 0;i < 2;i++)
2743 node = portal->nodes[i];
2745 portalpointer = (void **) &node->portals;
2750 Host_Error("RemovePortalFromNodes: portal not in leaf");
2754 if (portal->nodes[0] == node)
2756 *portalpointer = portal->next[0];
2757 portal->nodes[0] = NULL;
2759 else if (portal->nodes[1] == node)
2761 *portalpointer = portal->next[1];
2762 portal->nodes[1] = NULL;
2765 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2769 if (t->nodes[0] == node)
2770 portalpointer = (void **) &t->next[0];
2771 else if (t->nodes[1] == node)
2772 portalpointer = (void **) &t->next[1];
2774 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2779 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2782 mnode_t *front, *back, *other_node;
2783 mplane_t clipplane, *plane;
2784 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2785 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2787 // if a leaf, we're done
2791 plane = node->plane;
2793 front = node->children[0];
2794 back = node->children[1];
2796 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2798 // create the new portal by generating a polygon for the node plane,
2799 // and clipping it by all of the other portals(which came from nodes above this one)
2800 nodeportal = AllocPortal();
2801 nodeportal->plane = *node->plane;
2803 nodeportalwinding = BaseWindingForPlane(node->plane);
2804 side = 0; // shut up compiler warning
2805 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2807 clipplane = portal->plane;
2808 if (portal->nodes[0] == portal->nodes[1])
2809 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2810 if (portal->nodes[0] == node)
2812 else if (portal->nodes[1] == node)
2814 clipplane.dist = -clipplane.dist;
2815 VectorNegate(clipplane.normal, clipplane.normal);
2819 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2821 nodeportalwinding = ClipWinding(nodeportalwinding, &clipplane, true);
2822 if (!nodeportalwinding)
2824 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2829 if (nodeportalwinding)
2831 // if the plane was not clipped on all sides, there was an error
2832 nodeportal->winding = nodeportalwinding;
2833 AddPortalToNodes(nodeportal, front, back);
2836 // split the portals of this node along this node's plane and assign them to the children of this node
2837 // (migrating the portals downward through the tree)
2838 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2840 if (portal->nodes[0] == portal->nodes[1])
2841 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2842 if (portal->nodes[0] == node)
2844 else if (portal->nodes[1] == node)
2847 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2848 nextportal = portal->next[side];
2850 other_node = portal->nodes[!side];
2851 RemovePortalFromNodes(portal);
2853 // cut the portal into two portals, one on each side of the node plane
2854 DivideWinding(portal->winding, plane, &frontwinding, &backwinding);
2859 AddPortalToNodes(portal, back, other_node);
2861 AddPortalToNodes(portal, other_node, back);
2867 AddPortalToNodes(portal, front, other_node);
2869 AddPortalToNodes(portal, other_node, front);
2873 // the winding is split
2874 splitportal = AllocPortal();
2875 temp = splitportal->chain;
2876 *splitportal = *portal;
2877 splitportal->chain = temp;
2878 splitportal->winding = backwinding;
2879 FreeWinding(portal->winding);
2880 portal->winding = frontwinding;
2884 AddPortalToNodes(portal, front, other_node);
2885 AddPortalToNodes(splitportal, back, other_node);
2889 AddPortalToNodes(portal, other_node, front);
2890 AddPortalToNodes(splitportal, other_node, back);
2894 Mod_Q1BSP_RecursiveNodePortals(front);
2895 Mod_Q1BSP_RecursiveNodePortals(back);
2899 static void Mod_Q1BSP_MakePortals(void)
2902 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2903 Mod_Q1BSP_FinalizePortals();
2906 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2909 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2910 msurface_t *surf, *s;
2911 float *v0, *v1, *v2, *v3;
2912 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2913 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2914 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2916 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)
2918 if (surf->neighborsurfaces[vertnum])
2920 surf->neighborsurfaces[vertnum] = NULL;
2921 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2923 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2924 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2925 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2928 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2929 if (s->neighborsurfaces[vnum] == surf)
2931 if (vnum < s->poly_numverts)
2933 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)
2935 if (s->neighborsurfaces[vnum] == NULL
2936 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2937 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2939 surf->neighborsurfaces[vertnum] = s;
2940 s->neighborsurfaces[vnum] = surf;
2944 if (vnum < s->poly_numverts)
2952 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2954 int i, j, stylecounts[256], totalcount, remapstyles[256];
2956 memset(stylecounts, 0, sizeof(stylecounts));
2957 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2959 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2960 for (j = 0;j < MAXLIGHTMAPS;j++)
2961 stylecounts[surf->styles[j]]++;
2964 model->brushq1.light_styles = 0;
2965 for (i = 0;i < 255;i++)
2969 remapstyles[i] = model->brushq1.light_styles++;
2970 totalcount += stylecounts[i] + 1;
2975 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2976 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2977 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2978 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2979 model->brushq1.light_styles = 0;
2980 for (i = 0;i < 255;i++)
2982 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2984 for (i = 0;i < model->brushq1.light_styles;i++)
2986 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2987 j += stylecounts[model->brushq1.light_style[i]] + 1;
2989 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2991 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2992 for (j = 0;j < MAXLIGHTMAPS;j++)
2993 if (surf->styles[j] != 255)
2994 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2997 for (i = 0;i < model->brushq1.light_styles;i++)
2999 *model->brushq1.light_styleupdatechains[i] = NULL;
3000 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
3001 j += stylecounts[model->brushq1.light_style[i]] + 1;
3005 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
3008 for (i = 0;i < model->brushq1.numtextures;i++)
3009 model->brushq1.pvstexturechainslength[i] = 0;
3010 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
3012 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
3014 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
3015 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
3018 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
3020 if (model->brushq1.pvstexturechainslength[i])
3022 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
3023 j += model->brushq1.pvstexturechainslength[i] + 1;
3026 model->brushq1.pvstexturechains[i] = NULL;
3028 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
3029 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
3030 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
3031 for (i = 0;i < model->brushq1.numtextures;i++)
3033 if (model->brushq1.pvstexturechainslength[i])
3035 *model->brushq1.pvstexturechains[i] = NULL;
3036 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
3041 void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
3050 // if this is a leaf, accumulate the pvs bits
3051 if (node->contents < 0)
3053 if (node->contents != CONTENTS_SOLID)
3055 pvs = model->brushq1.LeafPVS(model, (mleaf_t *)node);
3056 for (i = 0;i < pvsbytes;i++)
3057 pvsbuffer[i] |= pvs[i];
3062 plane = node->plane;
3063 d = DotProduct(org, plane->normal) - plane->dist;
3065 node = node->children[0];
3066 else if (d < -radius)
3067 node = node->children[1];
3070 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
3071 node = node->children[1];
3076 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
3077 //of the given point.
3078 int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
3080 int bytes = (sv.worldmodel->brushq1.numleafs+31)>>3;
3081 if (bytes > pvsbufferlength)
3082 bytes = pvsbufferlength;
3083 memset(pvsbuffer, 0, bytes);
3084 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, sv.worldmodel->brushq1.nodes);
3088 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
3089 extern void R_Model_Brush_Draw(entity_render_t *ent);
3090 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
3091 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);
3092 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
3097 mempool_t *mainmempool;
3099 model_t *originalloadmodel;
3100 float dist, modelyawradius, modelradius, *vec;
3104 mod->type = mod_brush;
3106 header = (dheader_t *)buffer;
3108 i = LittleLong(header->version);
3109 if (i != BSPVERSION && i != 30)
3110 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
3111 mod->brushq1.ishlbsp = i == 30;
3113 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
3114 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
3115 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
3116 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
3117 mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
3118 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
3119 mod->brushq1.LeafPVS = Mod_Q1BSP_LeafPVS;
3120 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
3122 if (loadmodel->isworldmodel)
3124 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
3125 // until we get a texture for it...
3129 // swap all the lumps
3130 mod_base = (qbyte *)header;
3132 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
3133 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3137 // store which lightmap format to use
3138 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3140 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
3141 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
3142 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
3143 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
3144 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
3145 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
3146 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
3147 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
3148 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
3149 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
3150 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
3151 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
3152 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
3153 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
3154 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
3156 Mod_Q1BSP_MakeHull0();
3157 Mod_Q1BSP_MakePortals();
3159 mod->numframes = 2; // regular and alternate animation
3161 mainmempool = mod->mempool;
3162 loadname = mod->name;
3164 Mod_Q1BSP_LoadLightList();
3165 originalloadmodel = loadmodel;
3168 // set up the submodels(FIXME: this is confusing)
3170 for (i = 0;i < mod->brushq1.numsubmodels;i++)
3172 bm = &mod->brushq1.submodels[i];
3174 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3175 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3177 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3178 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3181 mod->brushq1.firstmodelsurface = bm->firstface;
3182 mod->brushq1.nummodelsurfaces = bm->numfaces;
3184 // this gets altered below if sky is used
3185 mod->DrawSky = NULL;
3186 mod->Draw = R_Model_Brush_Draw;
3187 mod->DrawFakeShadow = NULL;
3188 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
3189 mod->DrawLight = R_Model_Brush_DrawLight;
3190 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
3191 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
3192 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
3193 Mod_Q1BSP_BuildPVSTextureChains(mod);
3194 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
3195 if (mod->brushq1.nummodelsurfaces)
3197 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3198 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3199 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3202 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
3204 // we only need to have a drawsky function if it is used(usually only on world model)
3205 if (surf->texinfo->texture->shader == &Cshader_sky)
3206 mod->DrawSky = R_Model_Brush_DrawSky;
3207 // LordHavoc: submodels always clip, even if water
3208 if (mod->brushq1.numsubmodels - 1)
3209 surf->flags |= SURF_SOLIDCLIP;
3210 // calculate bounding shapes
3211 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
3213 for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
3215 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3216 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3217 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3218 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3219 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3220 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3221 dist = vec[0]*vec[0]+vec[1]*vec[1];
3222 if (modelyawradius < dist)
3223 modelyawradius = dist;
3224 dist += vec[2]*vec[2];
3225 if (modelradius < dist)
3230 modelyawradius = sqrt(modelyawradius);
3231 modelradius = sqrt(modelradius);
3232 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3233 mod->yawmins[2] = mod->normalmins[2];
3234 mod->yawmaxs[2] = mod->normalmaxs[2];
3235 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3236 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3237 mod->radius = modelradius;
3238 mod->radius2 = modelradius * modelradius;
3242 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3243 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
3245 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
3247 mod->brushq1.numleafs = bm->visleafs;
3249 // LordHavoc: only register submodels if it is the world
3250 // (prevents bsp models from replacing world submodels)
3251 if (loadmodel->isworldmodel && i < (mod->brushq1.numsubmodels - 1))
3254 // duplicate the basic information
3255 sprintf(name, "*%i", i+1);
3256 loadmodel = Mod_FindName(name);
3258 strcpy(loadmodel->name, name);
3259 // textures and memory belong to the main model
3260 loadmodel->texturepool = NULL;
3261 loadmodel->mempool = NULL;
3266 loadmodel = originalloadmodel;
3267 //Mod_Q1BSP_ProcessLightList();
3270 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3274 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3281 in = (void *)(mod_base + l->fileofs);
3282 if (l->filelen % sizeof(*in))
3283 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3284 count = l->filelen / sizeof(*in);
3285 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3288 loadmodel->num = count;
3290 for (i = 0;i < count;i++, in++, out++)
3296 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3303 in = (void *)(mod_base + l->fileofs);
3304 if (l->filelen % sizeof(*in))
3305 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3306 count = l->filelen / sizeof(*in);
3307 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3310 loadmodel->num = count;
3312 for (i = 0;i < count;i++, in++, out++)
3318 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3325 in = (void *)(mod_base + l->fileofs);
3326 if (l->filelen % sizeof(*in))
3327 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3328 count = l->filelen / sizeof(*in);
3329 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3332 loadmodel->num = count;
3334 for (i = 0;i < count;i++, in++, out++)
3340 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3347 in = (void *)(mod_base + l->fileofs);
3348 if (l->filelen % sizeof(*in))
3349 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3350 count = l->filelen / sizeof(*in);
3351 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3354 loadmodel->num = count;
3356 for (i = 0;i < count;i++, in++, out++)
3362 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3369 in = (void *)(mod_base + l->fileofs);
3370 if (l->filelen % sizeof(*in))
3371 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3372 count = l->filelen / sizeof(*in);
3373 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3376 loadmodel->num = count;
3378 for (i = 0;i < count;i++, in++, out++)
3384 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3391 in = (void *)(mod_base + l->fileofs);
3392 if (l->filelen % sizeof(*in))
3393 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3394 count = l->filelen / sizeof(*in);
3395 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3398 loadmodel->num = count;
3400 for (i = 0;i < count;i++, in++, out++)
3406 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3413 in = (void *)(mod_base + l->fileofs);
3414 if (l->filelen % sizeof(*in))
3415 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3416 count = l->filelen / sizeof(*in);
3417 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3420 loadmodel->num = count;
3422 for (i = 0;i < count;i++, in++, out++)
3428 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3435 in = (void *)(mod_base + l->fileofs);
3436 if (l->filelen % sizeof(*in))
3437 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3438 count = l->filelen / sizeof(*in);
3439 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3442 loadmodel->num = count;
3444 for (i = 0;i < count;i++, in++, out++)
3450 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3457 in = (void *)(mod_base + l->fileofs);
3458 if (l->filelen % sizeof(*in))
3459 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3460 count = l->filelen / sizeof(*in);
3461 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3464 loadmodel->num = count;
3466 for (i = 0;i < count;i++, in++, out++)
3472 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3479 in = (void *)(mod_base + l->fileofs);
3480 if (l->filelen % sizeof(*in))
3481 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3482 count = l->filelen / sizeof(*in);
3483 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3486 loadmodel->num = count;
3488 for (i = 0;i < count;i++, in++, out++)
3494 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3501 in = (void *)(mod_base + l->fileofs);
3502 if (l->filelen % sizeof(*in))
3503 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3504 count = l->filelen / sizeof(*in);
3505 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3508 loadmodel->num = count;
3510 for (i = 0;i < count;i++, in++, out++)
3516 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3523 in = (void *)(mod_base + l->fileofs);
3524 if (l->filelen % sizeof(*in))
3525 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3526 count = l->filelen / sizeof(*in);
3527 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3530 loadmodel->num = count;
3532 for (i = 0;i < count;i++, in++, out++)
3538 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3545 in = (void *)(mod_base + l->fileofs);
3546 if (l->filelen % sizeof(*in))
3547 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3548 count = l->filelen / sizeof(*in);
3549 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3552 loadmodel->num = count;
3554 for (i = 0;i < count;i++, in++, out++)
3560 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3567 in = (void *)(mod_base + l->fileofs);
3568 if (l->filelen % sizeof(*in))
3569 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3570 count = l->filelen / sizeof(*in);
3571 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3574 loadmodel->num = count;
3576 for (i = 0;i < count;i++, in++, out++)
3582 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3589 in = (void *)(mod_base + l->fileofs);
3590 if (l->filelen % sizeof(*in))
3591 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3592 count = l->filelen / sizeof(*in);
3593 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3596 loadmodel->num = count;
3598 for (i = 0;i < count;i++, in++, out++)
3604 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3611 in = (void *)(mod_base + l->fileofs);
3612 if (l->filelen % sizeof(*in))
3613 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3614 count = l->filelen / sizeof(*in);
3615 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3618 loadmodel->num = count;
3620 for (i = 0;i < count;i++, in++, out++)
3626 static void Mod_Q2BSP_LoadModels(lump_t *l)
3633 in = (void *)(mod_base + l->fileofs);
3634 if (l->filelen % sizeof(*in))
3635 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3636 count = l->filelen / sizeof(*in);
3637 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3640 loadmodel->num = count;
3642 for (i = 0;i < count;i++, in++, out++)
3648 void Mod_Q2BSP_Load(model_t *mod, void *buffer)
3651 q2dheader_t *header;
3653 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3655 mod->type = mod_brushq2;
3657 header = (q2dheader_t *)buffer;
3659 i = LittleLong(header->version);
3660 if (i != Q2BSPVERSION)
3661 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3662 mod->brushq1.ishlbsp = false;
3663 if (loadmodel->isworldmodel)
3665 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
3666 // until we get a texture for it...
3670 mod_base = (qbyte *)header;
3672 // swap all the lumps
3673 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3674 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3676 // store which lightmap format to use
3677 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3679 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3680 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3681 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3682 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3683 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3684 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3685 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3686 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3687 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3688 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3689 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3690 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3691 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3692 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3693 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3694 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3695 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3696 // LordHavoc: must go last because this makes the submodels
3697 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3701 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3704 char key[128], value[4096];
3706 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3707 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3708 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3711 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3712 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3713 data = loadmodel->brush.entities;
3714 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3715 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3719 if (!COM_ParseToken(&data, false))
3721 if (com_token[0] == '}')
3722 break; // end of worldspawn
3723 if (com_token[0] == '_')
3724 strcpy(key, com_token + 1);
3726 strcpy(key, com_token);
3727 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3728 key[strlen(key)-1] = 0;
3729 if (!COM_ParseToken(&data, false))
3731 strcpy(value, com_token);
3732 if (!strcmp("gridsize", key))
3734 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3735 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3741 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3747 in = (void *)(mod_base + l->fileofs);
3748 if (l->filelen % sizeof(*in))
3749 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3750 count = l->filelen / sizeof(*in);
3751 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3753 loadmodel->brushq3.data_textures = out;
3754 loadmodel->brushq3.num_textures = count;
3756 for (i = 0;i < count;i++, in++, out++)
3758 strncpy(out->name, in->name, sizeof(out->name) - 1);
3759 out->surfaceflags = LittleLong(in->surfaceflags);
3760 out->contents = LittleLong(in->contents);
3763 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3767 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3773 in = (void *)(mod_base + l->fileofs);
3774 if (l->filelen % sizeof(*in))
3775 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3776 count = l->filelen / sizeof(*in);
3777 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3779 loadmodel->brushq3.data_planes = out;
3780 loadmodel->brushq3.num_planes = count;
3782 for (i = 0;i < count;i++, in++, out++)
3784 out->normal[0] = LittleLong(in->normal[0]);
3785 out->normal[1] = LittleLong(in->normal[1]);
3786 out->normal[2] = LittleLong(in->normal[2]);
3787 out->dist = LittleLong(in->dist);
3792 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3795 q3mbrushside_t *out;
3798 in = (void *)(mod_base + l->fileofs);
3799 if (l->filelen % sizeof(*in))
3800 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3801 count = l->filelen / sizeof(*in);
3802 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3804 loadmodel->brushq3.data_brushsides = out;
3805 loadmodel->brushq3.num_brushsides = count;
3807 for (i = 0;i < count;i++, in++, out++)
3809 n = LittleLong(in->planeindex);
3810 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3811 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3812 out->plane = loadmodel->brushq3.data_planes + n;
3813 n = LittleLong(in->textureindex);
3814 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3815 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3816 out->texture = loadmodel->brushq3.data_textures + n;
3820 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3824 int i, j, k, m, n, c, count, numpoints, numplanes;
3826 colpointf_t pointsbuf[256*3];
3827 colplanef_t planesbuf[256], colplanef;
3829 in = (void *)(mod_base + l->fileofs);
3830 if (l->filelen % sizeof(*in))
3831 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3832 count = l->filelen / sizeof(*in);
3833 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3835 loadmodel->brushq3.data_brushes = out;
3836 loadmodel->brushq3.num_brushes = count;
3838 for (i = 0;i < count;i++, in++, out++)
3840 n = LittleLong(in->firstbrushside);
3841 c = LittleLong(in->numbrushsides);
3842 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3843 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3844 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3845 out->numbrushsides = c;
3846 n = LittleLong(in->textureindex);
3847 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3848 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3849 out->texture = loadmodel->brushq3.data_textures + n;
3851 // construct a collision brush, which needs points and planes...
3852 // each point and plane should be unique, and they don't refer to
3853 // eachother in any way, so keeping them unique is fairly easy
3856 for (j = 0;j < out->numbrushsides;j++)
3858 // create a huge polygon for the plane
3859 w = BaseWindingForPlane(out->firstbrushside[j].plane);
3860 // clip it by all other planes
3861 for (k = 0;k < out->numbrushsides && w;k++)
3863 w = ClipWinding(w, out->firstbrushside[k].plane, true);
3864 // if nothing is left, skip it
3865 // FIXME: should keep count of how many were skipped and report
3866 // it, just for sake of statistics
3869 // add the points uniquely (no duplicates)
3870 for (k = 0;k < w->numpoints;k++)
3872 for (m = 0;m < numpoints;m++)
3873 if (VectorDistance2(w->points[k * 3], pointsbuf[m * 3].v) < DIST_EPSILON)
3877 // check if there are too many and skip the brush
3878 if (numpoints >= 256)
3880 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many points for buffer\n");
3882 goto failedtomakecolbrush;
3885 VectorCopy(w->points[k * 3], pointsbuf[numpoints * 3].v);
3889 // add the plane uniquely (no duplicates)
3890 memset(&colplanef, 0, sizeof(colplanef));
3891 VectorCopy(out->firstbrushside[k].plane->normal, colplanef.normal);
3892 colplanef.dist = out->firstbrushside[k].plane->dist;
3893 for (k = 0;k < numplanes;k++)
3894 if (VectorCompare(planesbuf[k].normal, colplanef.normal) && planesbuf[k].dist == colplanef.dist)
3898 // check if there are too many and skip the brush
3899 if (numplanes >= 256)
3901 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many planes for buffer\n");
3903 goto failedtomakecolbrush;
3906 planesbuf[numplanes++] = colplanef;
3910 // if anything is left, create the collision brush
3911 if (numplanes && numpoints)
3913 out->colbrushf = Collision_AllocBrushFloat(loadmodel->mempool, numpoints, numplanes);
3914 memcpy(out->colbrushf->points, pointsbuf, numpoints * sizeof(float[3]));
3915 memcpy(out->colbrushf->planes, planesbuf, numplanes * sizeof(mplane_t));
3917 // return from errors to here
3918 failedtomakecolbrush:;
3922 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3928 in = (void *)(mod_base + l->fileofs);
3929 if (l->filelen % sizeof(*in))
3930 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3931 count = l->filelen / sizeof(*in);
3932 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3934 loadmodel->brushq3.data_effects = out;
3935 loadmodel->brushq3.num_effects = count;
3937 for (i = 0;i < count;i++, in++, out++)
3939 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3940 n = LittleLong(in->brushindex);
3941 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3942 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3943 out->brush = loadmodel->brushq3.data_brushes + n;
3944 out->unknown = LittleLong(in->unknown);
3948 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3953 in = (void *)(mod_base + l->fileofs);
3954 if (l->filelen % sizeof(*in))
3955 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3956 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3957 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3958 loadmodel->brushq3.data_texturetexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3959 loadmodel->brushq3.data_lightmaptexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3960 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3961 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3962 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3963 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3965 for (i = 0;i < count;i++, in++)
3967 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3968 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3969 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3970 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3971 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3972 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3973 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3974 // svector/tvector are calculated later in face loading
3975 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3976 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3977 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3978 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3979 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3980 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3981 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3982 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3983 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3984 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3985 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3986 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3987 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3991 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3997 in = (void *)(mod_base + l->fileofs);
3998 if (l->filelen % sizeof(int[3]))
3999 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4000 count = l->filelen / sizeof(*in);
4001 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4003 loadmodel->brushq3.num_triangles = count / 3;
4004 loadmodel->brushq3.data_element3i = out;
4005 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4007 for (i = 0;i < count;i++, in++, out++)
4009 *out = LittleLong(*in);
4010 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4011 Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices);
4015 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
4021 in = (void *)(mod_base + l->fileofs);
4022 if (l->filelen % sizeof(*in))
4023 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4024 count = l->filelen / sizeof(*in);
4025 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4027 loadmodel->brushq3.data_lightmaps = out;
4028 loadmodel->brushq3.num_lightmaps = count;
4030 for (i = 0;i < count;i++, in++, out++)
4031 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4034 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4038 int i, j, n, count, invalidelements, patchsize[2];
4040 in = (void *)(mod_base + l->fileofs);
4041 if (l->filelen % sizeof(*in))
4042 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4043 count = l->filelen / sizeof(*in);
4044 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4046 loadmodel->brushq3.data_faces = out;
4047 loadmodel->brushq3.num_faces = count;
4049 for (i = 0;i < count;i++, in++, out++)
4051 // check face type first
4052 out->type = LittleLong(in->type);
4053 if (out->type != Q3FACETYPE_POLYGON
4054 && out->type != Q3FACETYPE_PATCH
4055 && out->type != Q3FACETYPE_MESH
4056 && out->type != Q3FACETYPE_FLARE)
4058 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
4059 out->type = 0; // error
4063 n = LittleLong(in->textureindex);
4064 if (n < 0 || n >= loadmodel->brushq3.num_textures)
4066 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
4067 out->type = 0; // error
4071 out->texture = loadmodel->brushq3.data_textures + n;
4072 n = LittleLong(in->effectindex);
4073 if (n < -1 || n >= loadmodel->brushq3.num_effects)
4075 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4081 out->effect = loadmodel->brushq3.data_effects + n;
4082 n = LittleLong(in->lightmapindex);
4083 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
4085 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4089 out->lightmaptexture = NULL;
4091 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4093 out->firstvertex = LittleLong(in->firstvertex);
4094 out->numvertices = LittleLong(in->numvertices);
4095 out->firstelement = LittleLong(in->firstelement);
4096 out->numelements = LittleLong(in->numelements);
4097 out->numtriangles = out->numelements / 3;
4098 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
4100 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);
4101 out->type = 0; // error
4104 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
4106 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);
4107 out->type = 0; // error
4110 if (out->numtriangles * 3 != out->numelements)
4112 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
4113 out->type = 0; // error
4118 case Q3FACETYPE_POLYGON:
4119 case Q3FACETYPE_MESH:
4120 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4121 out->data_texturetexcoord2f = loadmodel->brushq3.data_texturetexcoord2f + out->firstvertex * 2;
4122 out->data_lightmaptexcoord2f = loadmodel->brushq3.data_lightmaptexcoord2f + out->firstvertex * 2;
4123 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4124 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4125 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4126 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4127 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4128 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4130 case Q3FACETYPE_PATCH:
4131 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4132 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4133 if (patchsize[0] < 1 || patchsize[1] < 1)
4135 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4136 out->type = 0; // error
4139 // FIXME: convert patch to triangles here!
4140 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
4144 case Q3FACETYPE_FLARE:
4145 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4150 for (j = 0, invalidelements = 0;j < out->numelements;j++)
4151 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
4153 if (invalidelements)
4155 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);
4156 for (j = 0;j < out->numelements;j++)
4158 Con_Printf(" %i", out->data_element3i[j]);
4159 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
4160 out->data_element3i[j] = 0;
4167 static void Mod_Q3BSP_LoadModels(lump_t *l)
4171 int i, j, n, c, count;
4173 in = (void *)(mod_base + l->fileofs);
4174 if (l->filelen % sizeof(*in))
4175 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4176 count = l->filelen / sizeof(*in);
4177 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4179 loadmodel->brushq3.data_models = out;
4180 loadmodel->brushq3.num_models = count;
4182 for (i = 0;i < count;i++, in++, out++)
4184 for (j = 0;j < 3;j++)
4186 out->mins[j] = LittleFloat(in->mins[j]);
4187 out->maxs[j] = LittleFloat(in->maxs[j]);
4189 n = LittleLong(in->firstface);
4190 c = LittleLong(in->numfaces);
4191 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4192 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4193 out->firstface = loadmodel->brushq3.data_faces + n;
4195 n = LittleLong(in->firstbrush);
4196 c = LittleLong(in->numbrushes);
4197 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4198 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4199 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4200 out->numbrushes = c;
4204 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4210 in = (void *)(mod_base + l->fileofs);
4211 if (l->filelen % sizeof(*in))
4212 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4213 count = l->filelen / sizeof(*in);
4214 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4216 loadmodel->brushq3.data_leafbrushes = out;
4217 loadmodel->brushq3.num_leafbrushes = count;
4219 for (i = 0;i < count;i++, in++, out++)
4221 n = LittleLong(*in);
4222 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4223 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4224 *out = loadmodel->brushq3.data_brushes + n;
4228 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4234 in = (void *)(mod_base + l->fileofs);
4235 if (l->filelen % sizeof(*in))
4236 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4237 count = l->filelen / sizeof(*in);
4238 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4240 loadmodel->brushq3.data_leaffaces = out;
4241 loadmodel->brushq3.num_leaffaces = count;
4243 for (i = 0;i < count;i++, in++, out++)
4245 n = LittleLong(*in);
4246 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4247 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4248 *out = loadmodel->brushq3.data_faces + n;
4252 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4256 int i, j, n, c, count;
4258 in = (void *)(mod_base + l->fileofs);
4259 if (l->filelen % sizeof(*in))
4260 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4261 count = l->filelen / sizeof(*in);
4262 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4264 loadmodel->brushq3.data_leafs = out;
4265 loadmodel->brushq3.num_leafs = count;
4267 for (i = 0;i < count;i++, in++, out++)
4269 out->isnode = false;
4271 out->clusterindex = LittleLong(in->clusterindex);
4272 out->areaindex = LittleLong(in->areaindex);
4273 for (j = 0;j < 3;j++)
4275 // yes the mins/maxs are ints
4276 out->mins[j] = LittleLong(in->mins[j]);
4277 out->maxs[j] = LittleLong(in->maxs[j]);
4279 n = LittleLong(in->firstleafface);
4280 c = LittleLong(in->numleaffaces);
4281 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4282 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4283 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4284 out->numleaffaces = c;
4285 n = LittleLong(in->firstleafbrush);
4286 c = LittleLong(in->numleafbrushes);
4287 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4288 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4289 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4290 out->numleafbrushes = c;
4294 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4297 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4298 node->parent = parent;
4301 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4302 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4306 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4312 in = (void *)(mod_base + l->fileofs);
4313 if (l->filelen % sizeof(*in))
4314 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4315 count = l->filelen / sizeof(*in);
4316 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4318 loadmodel->brushq3.data_nodes = out;
4319 loadmodel->brushq3.num_nodes = count;
4321 for (i = 0;i < count;i++, in++, out++)
4325 n = LittleLong(in->planeindex);
4326 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4327 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4328 out->plane = loadmodel->brushq3.data_planes + n;
4329 for (j = 0;j < 2;j++)
4331 n = LittleLong(in->childrenindex[j]);
4334 if (n >= loadmodel->brushq3.num_nodes)
4335 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4336 out->children[j] = loadmodel->brushq3.data_nodes + n;
4341 if (n >= loadmodel->brushq3.num_leafs)
4342 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4343 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4346 // we don't load the mins/maxs
4349 // set the parent pointers
4350 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4353 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4356 q3dlightgrid_t *out;
4359 in = (void *)(mod_base + l->fileofs);
4360 if (l->filelen % sizeof(*in))
4361 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4362 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4363 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4364 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4365 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4366 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4367 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4368 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4369 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4370 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4371 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4372 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4373 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4374 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4375 if (l->filelen < count * (int)sizeof(*in))
4376 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]);
4377 if (l->filelen != count * (int)sizeof(*in))
4378 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4380 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4381 loadmodel->brushq3.data_lightgrid = out;
4382 loadmodel->brushq3.num_lightgrid = count;
4384 // no swapping or validation necessary
4385 memcpy(out, in, count * (int)sizeof(*out));
4387 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]);
4388 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]);
4391 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4396 in = (void *)(mod_base + l->fileofs);
4398 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4400 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4401 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4402 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4403 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4404 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4405 if (l->filelen < totalchains + (int)sizeof(*in))
4406 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);
4408 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4409 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4412 void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4414 // FIXME: finish this code
4415 VectorCopy(in, out);
4418 void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end)
4422 // recurse down node sides
4425 colpointf_t *ps, *pe;
4426 // FIXME? if TraceBrushPolygonTransform were to be made usable, the
4427 // node planes would need to be transformed too
4428 dist = node->plane->dist - (1.0f / 8.0f);
4429 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4431 if (DotProduct(ps->v, node->plane->normal) > dist || DotProduct(pe->v, node->plane->normal) > dist)
4433 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end);
4437 dist = node->plane->dist + (1.0f / 8.0f);
4438 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4440 if (DotProduct(ps->v, node->plane->normal) < dist || DotProduct(pe->v, node->plane->normal) < dist)
4442 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end);
4447 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4449 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4451 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4458 leaf = (q3mleaf_t *)node;
4459 for (i = 0;i < leaf->numleafbrushes;i++)
4460 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4464 void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4466 // FIXME: write this
4467 ambientcolor[0] += 255;
4468 ambientcolor[1] += 255;
4469 ambientcolor[2] += 255;
4472 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)
4475 colbrushf_t *thisbrush_start, *thisbrush_end;
4476 matrix4x4_t startmatrix, endmatrix;
4477 // FIXME: finish this code
4478 Matrix4x4_CreateIdentity(&startmatrix);
4479 Matrix4x4_CreateIdentity(&endmatrix);
4480 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4481 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4482 memset(trace, 0, sizeof(*trace));
4483 trace->fraction = 1;
4484 if (model->brushq3.num_nodes)
4485 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end);
4487 for (i = 0;i < model->brushq3.num_brushes;i++)
4488 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_brushes[i].colbrushf, model->brushq3.data_brushes[i].colbrushf);
4492 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)
4499 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4500 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4503 // node - recurse down the BSP tree
4504 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4507 node = node->children[0];
4510 node = node->children[1];
4512 default: // crossing
4513 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4515 node = node->children[1];
4522 int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4524 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4527 int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4529 // FIXME: write this
4530 memset(pvsbuffer, 0xFF, pvsbufferlength);
4531 return pvsbufferlength;
4534 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4537 q3dheader_t *header;
4539 mod->type = mod_brushq2;
4541 header = (q3dheader_t *)buffer;
4543 i = LittleLong(header->version);
4544 if (i != Q3BSPVERSION)
4545 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4546 if (loadmodel->isworldmodel)
4548 Cvar_SetValue("halflifebsp", false);
4549 // until we get a texture for it...
4553 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4554 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4555 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4556 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4557 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4558 //mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
4559 //mod->brushq1.LeafPVS = Mod_Q1BSP_LeafPVS;
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");