]> git.xonotic.org Git - xonotic/darkplaces.git/blob - model_brush.c
more hackish cleanups of PVS stuff, now decompresses the PVS data at load and this...
[xonotic/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24
25 // note: model_shared.c sets up r_notexture, and r_surf_notexture
26
27 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
28
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"};
36
37 void Mod_BrushInit(void)
38 {
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));
47 }
48
49 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
50 {
51         mnode_t *node;
52
53         if (model == NULL)
54                 return NULL;
55
56         Mod_CheckLoaded(model);
57
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];
63
64         return (mleaf_t *)node;
65 }
66
67
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)
69 {
70         int leafnum;
71 loc0:
72         if (node->contents < 0)
73         {
74                 // leaf
75                 if (node->contents == CONTENTS_SOLID)
76                         return false;
77                 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
78                 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
79         }
80
81         // node - recurse down the BSP tree
82         switch (BoxOnPlaneSide(mins, maxs, node->plane))
83         {
84         case 1: // front
85                 node = node->children[0];
86                 goto loc0;
87         case 2: // back
88                 node = node->children[1];
89                 goto loc0;
90         default: // crossing
91                 if (node->children[0]->contents != CONTENTS_SOLID)
92                         if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
93                                 return true;
94                 node = node->children[1];
95                 goto loc0;
96         }
97         // never reached
98         return false;
99 }
100
101 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
102 {
103         return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
104 }
105
106 /*
107 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
108 {
109         mnode_t *node;
110
111         if (model == NULL)
112                 return CONTENTS_EMPTY;
113
114         Mod_CheckLoaded(model);
115
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];
121
122         return ((mleaf_t *)node)->contents;
123 }
124 */
125
126 typedef struct findnonsolidlocationinfo_s
127 {
128         vec3_t center;
129         vec_t radius;
130         vec3_t nudge;
131         vec_t bestdist;
132         model_t *model;
133 }
134 findnonsolidlocationinfo_t;
135
136 #if 0
137 extern cvar_t samelevel;
138 #endif
139 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
140 {
141         int i, surfnum, k, *tri, *mark;
142         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
143 #if 0
144         float surfnormal[3];
145 #endif
146         msurface_t *surf;
147         surfmesh_t *mesh;
148         for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
149         {
150                 surf = info->model->brushq1.surfaces + *mark;
151                 if (surf->flags & SURF_SOLIDCLIP)
152                 {
153 #if 0
154                         VectorCopy(surf->plane->normal, surfnormal);
155                         if (surf->flags & SURF_PLANEBACK)
156                                 VectorNegate(surfnormal, surfnormal);
157 #endif
158                         for (mesh = surf->mesh;mesh;mesh = mesh->chain)
159                         {
160                                 for (k = 0;k < mesh->numtriangles;k++)
161                                 {
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])
170                                         {
171                                                 VectorNormalize(facenormal);
172 #if 0
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]);
175 #endif
176                                                 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
177                                                 if (f <= info->bestdist && f >= -info->bestdist)
178                                                 {
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]);
186 #if 0
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]);
198 #endif
199                                                         // face distance
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]))
203                                                         {
204                                                                 // we got lucky, the center is within the face
205                                                                 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
206                                                                 if (dist < 0)
207                                                                 {
208                                                                         dist = -dist;
209                                                                         if (info->bestdist > dist)
210                                                                         {
211                                                                                 info->bestdist = dist;
212                                                                                 VectorScale(facenormal, (info->radius - -dist), info->nudge);
213                                                                         }
214                                                                 }
215                                                                 else
216                                                                 {
217                                                                         if (info->bestdist > dist)
218                                                                         {
219                                                                                 info->bestdist = dist;
220                                                                                 VectorScale(facenormal, (info->radius - dist), info->nudge);
221                                                                         }
222                                                                 }
223                                                         }
224                                                         else
225                                                         {
226                                                                 // check which edge or vertex the center is nearest
227                                                                 for (i = 0;i < 3;i++)
228                                                                 {
229                                                                         f = DotProduct(info->center, edge[i]);
230                                                                         if (f >= DotProduct(vert[0], edge[i])
231                                                                          && f <= DotProduct(vert[1], edge[i]))
232                                                                         {
233                                                                                 // on edge
234                                                                                 VectorMA(info->center, -f, edge[i], point);
235                                                                                 dist = sqrt(DotProduct(point, point));
236                                                                                 if (info->bestdist > dist)
237                                                                                 {
238                                                                                         info->bestdist = dist;
239                                                                                         VectorScale(point, (info->radius / dist), info->nudge);
240                                                                                 }
241                                                                                 // skip both vertex checks
242                                                                                 // (both are further away than this edge)
243                                                                                 i++;
244                                                                         }
245                                                                         else
246                                                                         {
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)
251                                                                                 {
252                                                                                         info->bestdist = dist;
253                                                                                         VectorScale(point, (info->radius / dist), info->nudge);
254                                                                                 }
255                                                                         }
256                                                                 }
257                                                         }
258                                                 }
259                                         }
260                                 }
261                         }
262                 }
263         }
264 }
265
266 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
267 {
268         if (node->contents)
269         {
270                 if (((mleaf_t *)node)->nummarksurfaces)
271                         Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
272         }
273         else
274         {
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]);
280         }
281 }
282
283 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
284 {
285         int i;
286         findnonsolidlocationinfo_t info;
287         if (model == NULL)
288         {
289                 VectorCopy(in, out);
290                 return;
291         }
292         VectorCopy(in, info.center);
293         info.radius = radius;
294         info.model = model;
295         i = 0;
296         do
297         {
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);
302         }
303         while (info.bestdist < radius && ++i < 10);
304         VectorCopy(info.center, out);
305 }
306
307 typedef struct
308 {
309         // the hull we're tracing through
310         const hull_t *hull;
311
312         // the trace structure to fill in
313         trace_t *trace;
314
315         // start, end, and end - start (in model space)
316         double start[3];
317         double end[3];
318         double dist[3];
319 }
320 RecursiveHullCheckTraceInfo_t;
321
322 // 1/32 epsilon to keep floating point happy
323 #define DIST_EPSILON (0.03125)
324
325 #define HULLCHECKSTATE_EMPTY 0
326 #define HULLCHECKSTATE_SOLID 1
327 #define HULLCHECKSTATE_DONE 2
328
329 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
330 {
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)
334         int ret;
335         mplane_t *plane;
336         double t1, t2;
337
338         // variables that need to be stored on the stack when recursing
339         dclipnode_t *node;
340         int side;
341         double midf, mid[3];
342
343         // LordHavoc: a goto!  everyone flee in terror... :)
344 loc0:
345         // check for empty
346         if (num < 0)
347         {
348                 t->trace->endcontents = num;
349                 if (t->trace->thiscontents)
350                 {
351                         if (num == t->trace->thiscontents)
352                                 t->trace->allsolid = false;
353                         else
354                         {
355                                 // if the first leaf is solid, set startsolid
356                                 if (t->trace->allsolid)
357                                         t->trace->startsolid = true;
358                                 return HULLCHECKSTATE_SOLID;
359                         }
360                         return HULLCHECKSTATE_EMPTY;
361                 }
362                 else
363                 {
364                         if (num != CONTENTS_SOLID)
365                         {
366                                 t->trace->allsolid = false;
367                                 if (num == CONTENTS_EMPTY)
368                                         t->trace->inopen = true;
369                                 else
370                                         t->trace->inwater = true;
371                         }
372                         else
373                         {
374                                 // if the first leaf is solid, set startsolid
375                                 if (t->trace->allsolid)
376                                         t->trace->startsolid = true;
377                                 return HULLCHECKSTATE_SOLID;
378                         }
379                         return HULLCHECKSTATE_EMPTY;
380                 }
381         }
382
383         // find the point distances
384         node = t->hull->clipnodes + num;
385
386         plane = t->hull->planes + node->planenum;
387         if (plane->type < 3)
388         {
389                 t1 = p1[plane->type] - plane->dist;
390                 t2 = p2[plane->type] - plane->dist;
391         }
392         else
393         {
394                 t1 = DotProduct (plane->normal, p1) - plane->dist;
395                 t2 = DotProduct (plane->normal, p2) - plane->dist;
396         }
397
398         if (t1 < 0)
399         {
400                 if (t2 < 0)
401                 {
402                         num = node->children[1];
403                         goto loc0;
404                 }
405                 side = 1;
406         }
407         else
408         {
409                 if (t2 >= 0)
410                 {
411                         num = node->children[0];
412                         goto loc0;
413                 }
414                 side = 0;
415         }
416
417         // the line intersects, find intersection point
418         // LordHavoc: this uses the original trace for maximum accuracy
419         if (plane->type < 3)
420         {
421                 t1 = t->start[plane->type] - plane->dist;
422                 t2 = t->end[plane->type] - plane->dist;
423         }
424         else
425         {
426                 t1 = DotProduct (plane->normal, t->start) - plane->dist;
427                 t2 = DotProduct (plane->normal, t->end) - plane->dist;
428         }
429
430         midf = t1 / (t1 - t2);
431         midf = bound(p1f, midf, p2f);
432         VectorMA(t->start, midf, t->dist, mid);
433
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)
438                 return ret;
439
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)
443                 return ret;
444
445         // front is air and back is solid, this is the impact point...
446         if (side)
447         {
448                 t->trace->plane.dist = -plane->dist;
449                 VectorNegate (plane->normal, t->trace->plane.normal);
450         }
451         else
452         {
453                 t->trace->plane.dist = plane->dist;
454                 VectorCopy (plane->normal, t->trace->plane.normal);
455         }
456
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);
460
461         midf = t1 / (t1 - t2);
462         t->trace->fraction = bound(0.0f, midf, 1.0);
463
464         return HULLCHECKSTATE_DONE;
465 }
466
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)
468 {
469         // this function currently only supports same size start and end
470         double boxsize[3];
471         RecursiveHullCheckTraceInfo_t rhc;
472
473         memset(&rhc, 0, sizeof(rhc));
474         memset(trace, 0, sizeof(trace_t));
475         rhc.trace = trace;
476         rhc.trace->fraction = 1;
477         rhc.trace->allsolid = true;
478         VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
479         if (boxsize[0] < 3)
480                 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
481         else if (model->brushq1.ishlbsp)
482         {
483                 if (boxsize[0] <= 32)
484                 {
485                         if (boxsize[2] < 54) // pick the nearest of 36 or 72
486                                 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
487                         else
488                                 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
489                 }
490                 else
491                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
492         }
493         else
494         {
495                 if (boxsize[0] <= 32)
496                         rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
497                 else
498                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
499         }
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);
504 }
505
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)
507 {
508         int side, distz = endz - startz;
509         float front, back;
510         float mid;
511
512 loc0:
513         if (node->contents < 0)
514                 return false;           // didn't hit anything
515
516         switch (node->plane->type)
517         {
518         case PLANE_X:
519                 node = node->children[x < node->plane->dist];
520                 goto loc0;
521         case PLANE_Y:
522                 node = node->children[y < node->plane->dist];
523                 goto loc0;
524         case PLANE_Z:
525                 side = startz < node->plane->dist;
526                 if ((endz < node->plane->dist) == side)
527                 {
528                         node = node->children[side];
529                         goto loc0;
530                 }
531                 // found an intersection
532                 mid = node->plane->dist;
533                 break;
534         default:
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)
540                 {
541                         node = node->children[side];
542                         goto loc0;
543                 }
544                 // found an intersection
545                 mid = startz + distz * (front - node->plane->dist) / (front - back);
546                 break;
547         }
548
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
552         else
553         {
554                 // check for impact on this node
555                 if (node->numsurfaces)
556                 {
557                         int i, ds, dt;
558                         msurface_t *surf;
559
560                         surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
561                         for (i = 0;i < node->numsurfaces;i++, surf++)
562                         {
563                                 if (!(surf->flags & SURF_LIGHTMAP))
564                                         continue;       // no lightmaps
565
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]);
568
569                                 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
570                                         continue;
571
572                                 ds -= surf->texturemins[0];
573                                 dt -= surf->texturemins[1];
574
575                                 if (ds > surf->extents[0] || dt > surf->extents[1])
576                                         continue;
577
578                                 if (surf->samples)
579                                 {
580                                         qbyte *lightmap;
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
584
585                                         lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
586
587                                         for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
588                                         {
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;
594                                                 lightmap += size3;
595                                         }
596
597 /*
598 LordHavoc: here's the readable version of the interpolation
599 code, not quite as easy for the compiler to optimize...
600
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
607
608 first we interpolate the top two points, to get the top
609 edge sample
610
611         r0 = (((r01-r00) * dsfrac) >> 4) + r00;
612         g0 = (((g01-g00) * dsfrac) >> 4) + g00;
613         b0 = (((b01-b00) * dsfrac) >> 4) + b00;
614
615 then we interpolate the bottom two points, to get the
616 bottom edge sample
617
618         r1 = (((r11-r10) * dsfrac) >> 4) + r10;
619         g1 = (((g11-g10) * dsfrac) >> 4) + g10;
620         b1 = (((b11-b10) * dsfrac) >> 4) + b10;
621
622 then we interpolate the top and bottom samples to get the
623 middle sample (the one which was requested)
624
625         r = (((r1-r0) * dtfrac) >> 4) + r0;
626         g = (((g1-g0) * dtfrac) >> 4) + g0;
627         b = (((b1-b0) * dtfrac) >> 4) + b0;
628 */
629
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);
633                                 }
634                                 return true; // success
635                         }
636                 }
637
638                 // go down back side
639                 node = node->children[side ^ 1];
640                 startz = mid;
641                 distz = endz - startz;
642                 goto loc0;
643         }
644 }
645
646 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
647 {
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);
649 }
650
651 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
652 {
653         int c;
654         while (out < outend)
655         {
656                 if (in == inend)
657                 {
658                         Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
659                         return;
660                 }
661                 c = *in++;
662                 if (c)
663                         *out++ = c;
664                 else
665                 {
666                         for (c = *in++;c > 0;c--)
667                         {
668                                 if (out == outend)
669                                 {
670                                         Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
671                                         return;
672                                 }
673                                 *out++ = 0;
674                         }
675                 }
676         }
677 }
678
679 static void Mod_Q1BSP_LoadTextures(lump_t *l)
680 {
681         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
682         miptex_t *dmiptex;
683         texture_t *tx, *tx2, *anims[10], *altanims[10];
684         dmiptexlump_t *m;
685         qbyte *data, *mtdata;
686         char name[256];
687
688         loadmodel->brushq1.textures = NULL;
689
690         if (!l->filelen)
691                 return;
692
693         m = (dmiptexlump_t *)(mod_base + l->fileofs);
694
695         m->nummiptex = LittleLong (m->nummiptex);
696
697         // add two slots for notexture walls and notexture liquids
698         loadmodel->brushq1.numtextures = m->nummiptex + 2;
699         loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
700
701         // fill out all slots with notexture
702         for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
703         {
704                 tx->number = i;
705                 strcpy(tx->name, "NO TEXTURE FOUND");
706                 tx->width = 16;
707                 tx->height = 16;
708                 tx->skin.base = r_notexture;
709                 tx->shader = &Cshader_wall_lightmap;
710                 tx->flags = SURF_SOLIDCLIP;
711                 if (i == loadmodel->brushq1.numtextures - 1)
712                 {
713                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
714                         tx->shader = &Cshader_water;
715                 }
716                 tx->currentframe = tx;
717         }
718
719         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
720         dofs = m->dataofs;
721         // LordHavoc: mostly rewritten map texture loader
722         for (i = 0;i < m->nummiptex;i++)
723         {
724                 dofs[i] = LittleLong(dofs[i]);
725                 if (dofs[i] == -1 || r_nosurftextures.integer)
726                         continue;
727                 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
728
729                 // make sure name is no more than 15 characters
730                 for (j = 0;dmiptex->name[j] && j < 15;j++)
731                         name[j] = dmiptex->name[j];
732                 name[j] = 0;
733
734                 mtwidth = LittleLong(dmiptex->width);
735                 mtheight = LittleLong(dmiptex->height);
736                 mtdata = NULL;
737                 j = LittleLong(dmiptex->offsets[0]);
738                 if (j)
739                 {
740                         // texture included
741                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
742                         {
743                                 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
744                                 continue;
745                         }
746                         mtdata = (qbyte *)dmiptex + j;
747                 }
748
749                 if ((mtwidth & 15) || (mtheight & 15))
750                         Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
751
752                 // LordHavoc: force all names to lowercase
753                 for (j = 0;name[j];j++)
754                         if (name[j] >= 'A' && name[j] <= 'Z')
755                                 name[j] += 'a' - 'A';
756
757                 tx = loadmodel->brushq1.textures + i;
758                 strcpy(tx->name, name);
759                 tx->width = mtwidth;
760                 tx->height = mtheight;
761
762                 if (!tx->name[0])
763                 {
764                         sprintf(tx->name, "unnamed%i", i);
765                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
766                 }
767
768                 // LordHavoc: HL sky textures are entirely different than quake
769                 if (!loadmodel->brushq1.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
770                 {
771                         if (loadmodel->isworldmodel)
772                         {
773                                 data = loadimagepixels(tx->name, false, 0, 0);
774                                 if (data)
775                                 {
776                                         if (image_width == 256 && image_height == 128)
777                                         {
778                                                 R_InitSky(data, 4);
779                                                 Mem_Free(data);
780                                         }
781                                         else
782                                         {
783                                                 Mem_Free(data);
784                                                 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
785                                                 if (mtdata != NULL)
786                                                         R_InitSky(mtdata, 1);
787                                         }
788                                 }
789                                 else if (mtdata != NULL)
790                                         R_InitSky(mtdata, 1);
791                         }
792                 }
793                 else
794                 {
795                         if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
796                         {
797                                 // did not find external texture, load it from the bsp or wad3
798                                 if (loadmodel->brushq1.ishlbsp)
799                                 {
800                                         // internal texture overrides wad
801                                         qbyte *pixels, *freepixels, *fogpixels;
802                                         pixels = freepixels = NULL;
803                                         if (mtdata)
804                                                 pixels = W_ConvertWAD3Texture(dmiptex);
805                                         if (pixels == NULL)
806                                                 pixels = freepixels = W_GetTexture(tx->name);
807                                         if (pixels != NULL)
808                                         {
809                                                 tx->width = image_width;
810                                                 tx->height = image_height;
811                                                 tx->skin.base = tx->skin.merged = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
812                                                 if (Image_CheckAlpha(pixels, image_width * image_height, true))
813                                                 {
814                                                         fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
815                                                         for (j = 0;j < image_width * image_height * 4;j += 4)
816                                                         {
817                                                                 fogpixels[j + 0] = 255;
818                                                                 fogpixels[j + 1] = 255;
819                                                                 fogpixels[j + 2] = 255;
820                                                                 fogpixels[j + 3] = pixels[j + 3];
821                                                         }
822                                                         tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
823                                                         Mem_Free(fogpixels);
824                                                 }
825                                         }
826                                         if (freepixels)
827                                                 Mem_Free(freepixels);
828                                 }
829                                 else if (mtdata) // texture included
830                                         Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
831                         }
832                 }
833                 if (tx->skin.base == NULL)
834                 {
835                         // no texture found
836                         tx->width = 16;
837                         tx->height = 16;
838                         tx->skin.base = r_notexture;
839                 }
840
841                 if (tx->name[0] == '*')
842                 {
843                         // turb does not block movement
844                         tx->flags &= ~SURF_SOLIDCLIP;
845                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
846                         // LordHavoc: some turbulent textures should be fullbright and solid
847                         if (!strncmp(tx->name,"*lava",5)
848                          || !strncmp(tx->name,"*teleport",9)
849                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
850                                 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
851                         else
852                                 tx->flags |= SURF_WATERALPHA;
853                         tx->shader = &Cshader_water;
854                 }
855                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
856                 {
857                         tx->flags |= SURF_DRAWSKY;
858                         tx->shader = &Cshader_sky;
859                 }
860                 else
861                 {
862                         tx->flags |= SURF_LIGHTMAP;
863                         if (!tx->skin.fog)
864                                 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
865                         tx->shader = &Cshader_wall_lightmap;
866                 }
867
868                 // start out with no animation
869                 tx->currentframe = tx;
870         }
871
872         // sequence the animations
873         for (i = 0;i < m->nummiptex;i++)
874         {
875                 tx = loadmodel->brushq1.textures + i;
876                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
877                         continue;
878                 if (tx->anim_total[0] || tx->anim_total[1])
879                         continue;       // already sequenced
880
881                 // find the number of frames in the animation
882                 memset(anims, 0, sizeof(anims));
883                 memset(altanims, 0, sizeof(altanims));
884
885                 for (j = i;j < m->nummiptex;j++)
886                 {
887                         tx2 = loadmodel->brushq1.textures + j;
888                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
889                                 continue;
890
891                         num = tx2->name[1];
892                         if (num >= '0' && num <= '9')
893                                 anims[num - '0'] = tx2;
894                         else if (num >= 'a' && num <= 'j')
895                                 altanims[num - 'a'] = tx2;
896                         else
897                                 Con_Printf("Bad animating texture %s\n", tx->name);
898                 }
899
900                 max = altmax = 0;
901                 for (j = 0;j < 10;j++)
902                 {
903                         if (anims[j])
904                                 max = j + 1;
905                         if (altanims[j])
906                                 altmax = j + 1;
907                 }
908                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
909
910                 incomplete = false;
911                 for (j = 0;j < max;j++)
912                 {
913                         if (!anims[j])
914                         {
915                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
916                                 incomplete = true;
917                         }
918                 }
919                 for (j = 0;j < altmax;j++)
920                 {
921                         if (!altanims[j])
922                         {
923                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
924                                 incomplete = true;
925                         }
926                 }
927                 if (incomplete)
928                         continue;
929
930                 if (altmax < 1)
931                 {
932                         // if there is no alternate animation, duplicate the primary
933                         // animation into the alternate
934                         altmax = max;
935                         for (k = 0;k < 10;k++)
936                                 altanims[k] = anims[k];
937                 }
938
939                 // link together the primary animation
940                 for (j = 0;j < max;j++)
941                 {
942                         tx2 = anims[j];
943                         tx2->animated = true;
944                         tx2->anim_total[0] = max;
945                         tx2->anim_total[1] = altmax;
946                         for (k = 0;k < 10;k++)
947                         {
948                                 tx2->anim_frames[0][k] = anims[k];
949                                 tx2->anim_frames[1][k] = altanims[k];
950                         }
951                 }
952
953                 // if there really is an alternate anim...
954                 if (anims[0] != altanims[0])
955                 {
956                         // link together the alternate animation
957                         for (j = 0;j < altmax;j++)
958                         {
959                                 tx2 = altanims[j];
960                                 tx2->animated = true;
961                                 // the primary/alternate are reversed here
962                                 tx2->anim_total[0] = altmax;
963                                 tx2->anim_total[1] = max;
964                                 for (k = 0;k < 10;k++)
965                                 {
966                                         tx2->anim_frames[0][k] = altanims[k];
967                                         tx2->anim_frames[1][k] = anims[k];
968                                 }
969                         }
970                 }
971         }
972 }
973
974 static void Mod_Q1BSP_LoadLighting(lump_t *l)
975 {
976         int i;
977         qbyte *in, *out, *data, d;
978         char litfilename[1024];
979         loadmodel->brushq1.lightdata = NULL;
980         if (loadmodel->brushq1.ishlbsp) // LordHavoc: load the colored lighting data straight
981         {
982                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
983                 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
984         }
985         else // LordHavoc: bsp version 29 (normal white lighting)
986         {
987                 // LordHavoc: hope is not lost yet, check for a .lit file to load
988                 strcpy(litfilename, loadmodel->name);
989                 FS_StripExtension(litfilename, litfilename);
990                 strcat(litfilename, ".lit");
991                 data = (qbyte*) FS_LoadFile(litfilename, false);
992                 if (data)
993                 {
994                         if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
995                         {
996                                 i = LittleLong(((int *)data)[1]);
997                                 if (i == 1)
998                                 {
999                                         Con_DPrintf("loaded %s\n", litfilename);
1000                                         loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1001                                         memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1002                                         Mem_Free(data);
1003                                         return;
1004                                 }
1005                                 else
1006                                 {
1007                                         Con_Printf("Unknown .lit file version (%d)\n", i);
1008                                         Mem_Free(data);
1009                                 }
1010                         }
1011                         else
1012                         {
1013                                 if (fs_filesize == 8)
1014                                         Con_Printf("Empty .lit file, ignoring\n");
1015                                 else
1016                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1017                                 Mem_Free(data);
1018                         }
1019                 }
1020                 // LordHavoc: oh well, expand the white lighting data
1021                 if (!l->filelen)
1022                         return;
1023                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1024                 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1025                 out = loadmodel->brushq1.lightdata;
1026                 memcpy(in, mod_base + l->fileofs, l->filelen);
1027                 for (i = 0;i < l->filelen;i++)
1028                 {
1029                         d = *in++;
1030                         *out++ = d;
1031                         *out++ = d;
1032                         *out++ = d;
1033                 }
1034         }
1035 }
1036
1037 static void Mod_Q1BSP_LoadLightList(void)
1038 {
1039         int a, n, numlights;
1040         char lightsfilename[1024], *s, *t, *lightsstring;
1041         mlight_t *e;
1042
1043         strcpy(lightsfilename, loadmodel->name);
1044         FS_StripExtension(lightsfilename, lightsfilename);
1045         strcat(lightsfilename, ".lights");
1046         s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1047         if (s)
1048         {
1049                 numlights = 0;
1050                 while (*s)
1051                 {
1052                         while (*s && *s != '\n')
1053                                 s++;
1054                         if (!*s)
1055                         {
1056                                 Mem_Free(lightsstring);
1057                                 Host_Error("lights file must end with a newline\n");
1058                         }
1059                         s++;
1060                         numlights++;
1061                 }
1062                 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1063                 s = lightsstring;
1064                 n = 0;
1065                 while (*s && n < numlights)
1066                 {
1067                         t = s;
1068                         while (*s && *s != '\n')
1069                                 s++;
1070                         if (!*s)
1071                         {
1072                                 Mem_Free(lightsstring);
1073                                 Host_Error("misparsed lights file!\n");
1074                         }
1075                         e = loadmodel->brushq1.lights + n;
1076                         *s = 0;
1077                         a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
1078                         *s = '\n';
1079                         if (a != 14)
1080                         {
1081                                 Mem_Free(lightsstring);
1082                                 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
1083                         }
1084                         s++;
1085                         n++;
1086                 }
1087                 if (*s)
1088                 {
1089                         Mem_Free(lightsstring);
1090                         Host_Error("misparsed lights file!\n");
1091                 }
1092                 loadmodel->brushq1.numlights = numlights;
1093                 Mem_Free(lightsstring);
1094         }
1095 }
1096
1097 /*
1098 static int castshadowcount = 0;
1099 static void Mod_Q1BSP_ProcessLightList(void)
1100 {
1101         int j, k, l, *mark, lnum;
1102         mlight_t *e;
1103         msurface_t *surf;
1104         float dist;
1105         mleaf_t *leaf;
1106         qbyte *pvs;
1107         vec3_t temp;
1108         float *v, radius2;
1109         for (lnum = 0, e = loadmodel->brushq1.lights;lnum < loadmodel->brushq1.numlights;lnum++, e++)
1110         {
1111                 e->cullradius2 = DotProduct(e->light, e->light) / (e->falloff * e->falloff * 8192.0f * 8192.0f * 2.0f * 2.0f);// + 4096.0f;
1112                 if (e->cullradius2 > 4096.0f * 4096.0f)
1113                         e->cullradius2 = 4096.0f * 4096.0f;
1114                 e->cullradius = e->lightradius = sqrt(e->cullradius2);
1115                 leaf = Mod_Q1BSP_PointInLeaf(e->origin, loadmodel);
1116                 if (leaf->compressed_vis)
1117                         pvs = Mod_Q1BSP_DecompressVis(leaf->compressed_vis, loadmodel);
1118                 else
1119                         pvs = mod_q1bsp_novis;
1120                 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
1121                         loadmodel->brushq1.surfacevisframes[j] = -1;
1122                 for (j = 0, leaf = loadmodel->brushq1.leafs + 1;j < loadmodel->brushq1.numleafs - 1;j++, leaf++)
1123                 {
1124                         if (pvs[j >> 3] & (1 << (j & 7)))
1125                         {
1126                                 for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++)
1127                                 {
1128                                         surf = loadmodel->brushq1.surfaces + *mark;
1129                                         if (surf->number != *mark)
1130                                                 Con_Printf("%d != %d\n", surf->number, *mark);
1131                                         dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1132                                         if (surf->flags & SURF_PLANEBACK)
1133                                                 dist = -dist;
1134                                         if (dist > 0 && dist < e->cullradius)
1135                                         {
1136                                                 temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
1137                                                 temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
1138                                                 temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
1139                                                 if (DotProduct(temp, temp) < lightradius2)
1140                                                         loadmodel->brushq1.surfacevisframes[*mark] = -2;
1141                                         }
1142                                 }
1143                         }
1144                 }
1145                 // build list of light receiving surfaces
1146                 e->numsurfaces = 0;
1147                 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
1148                         if (loadmodel->brushq1.surfacevisframes[j] == -2)
1149                                 e->numsurfaces++;
1150                 e->surfaces = NULL;
1151                 if (e->numsurfaces > 0)
1152                 {
1153                         e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
1154                         e->numsurfaces = 0;
1155                         for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
1156                                 if (loadmodel->brushq1.surfacevisframes[j] == -2)
1157                                         e->surfaces[e->numsurfaces++] = loadmodel->brushq1.surfaces + j;
1158                 }
1159                 // find bounding box and sphere of lit surfaces
1160                 // (these will be used for creating a shape to clip the light)
1161                 radius2 = 0;
1162                 for (j = 0;j < e->numsurfaces;j++)
1163                 {
1164                         surf = e->surfaces[j];
1165                         if (j == 0)
1166                         {
1167                                 VectorCopy(surf->poly_verts, e->mins);
1168                                 VectorCopy(surf->poly_verts, e->maxs);
1169                         }
1170                         for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
1171                         {
1172                                 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
1173                                 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
1174                                 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
1175                                 VectorSubtract(v, e->origin, temp);
1176                                 dist = DotProduct(temp, temp);
1177                                 if (radius2 < dist)
1178                                         radius2 = dist;
1179                         }
1180                 }
1181                 if (e->cullradius2 > radius2)
1182                 {
1183                         e->cullradius2 = radius2;
1184                         e->cullradius = sqrt(e->cullradius2);
1185                 }
1186                 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
1187                 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
1188                 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
1189                 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
1190                 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
1191                 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
1192                 // clip shadow volumes against eachother to remove unnecessary
1193                 // polygons(and sections of polygons)
1194                 {
1195                         //vec3_t polymins, polymaxs;
1196                         int maxverts = 4;
1197                         float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1198                         float f, *v0, *v1, projectdistance;
1199
1200                         e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 1024);
1201 #if 0
1202                         {
1203                         vec3_t outermins, outermaxs, innermins, innermaxs;
1204                         innermins[0] = e->mins[0] - 1;
1205                         innermins[1] = e->mins[1] - 1;
1206                         innermins[2] = e->mins[2] - 1;
1207                         innermaxs[0] = e->maxs[0] + 1;
1208                         innermaxs[1] = e->maxs[1] + 1;
1209                         innermaxs[2] = e->maxs[2] + 1;
1210                         outermins[0] = loadmodel->normalmins[0] - 1;
1211                         outermins[1] = loadmodel->normalmins[1] - 1;
1212                         outermins[2] = loadmodel->normalmins[2] - 1;
1213                         outermaxs[0] = loadmodel->normalmaxs[0] + 1;
1214                         outermaxs[1] = loadmodel->normalmaxs[1] + 1;
1215                         outermaxs[2] = loadmodel->normalmaxs[2] + 1;
1216                         // add bounding box around the whole shadow volume set,
1217                         // facing inward to limit light area, with an outer bounding box
1218                         // facing outward (this is needed by the shadow rendering method)
1219                         // X major
1220                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1221                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1222                         verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1223                         verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1224                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1225                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1226                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1227                         verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1228                         verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1229                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1230                         // X minor
1231                         verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1232                         verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1233                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1234                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1235                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1236                         verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1237                         verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1238                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1239                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1240                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1241                         // Y major
1242                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1243                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1244                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1245                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1246                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1247                         verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1248                         verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1249                         verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1250                         verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1251                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1252                         // Y minor
1253                         verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1254                         verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1255                         verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1256                         verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1257                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1258                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1259                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1260                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1261                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1262                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1263                         // Z major
1264                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1265                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
1266                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
1267                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1268                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1269                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1270                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
1271                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
1272                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1273                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1274                         // Z minor
1275                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
1276                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1277                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1278                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
1279                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1280                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
1281                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1282                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1283                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
1284                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1285                         }
1286 #endif
1287                         castshadowcount++;
1288                         for (j = 0;j < e->numsurfaces;j++)
1289                         {
1290                                 surf = e->surfaces[j];
1291                                 if (surf->flags & SURF_SHADOWCAST)
1292                                         surf->castshadow = castshadowcount;
1293                         }
1294                         for (j = 0;j < e->numsurfaces;j++)
1295                         {
1296                                 surf = e->surfaces[j];
1297                                 if (surf->castshadow != castshadowcount)
1298                                         continue;
1299                                 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1300                                 if (surf->flags & SURF_PLANEBACK)
1301                                         f = -f;
1302                                 projectdistance = e->lightradius;
1303                                 if (maxverts < surf->poly_numverts)
1304                                 {
1305                                         maxverts = surf->poly_numverts;
1306                                         if (verts)
1307                                                 Mem_Free(verts);
1308                                         verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1309                                 }
1310                                 // copy the original polygon, for the front cap of the volume
1311                                 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1312                                         VectorCopy(v0, v1);
1313                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1314                                 // project the original polygon, reversed, for the back cap of the volume
1315                                 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1316                                 {
1317                                         VectorSubtract(v0, e->origin, temp);
1318                                         VectorNormalize(temp);
1319                                         VectorMA(v0, projectdistance, temp, v1);
1320                                 }
1321                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1322                                 // project the shadow volume sides
1323                                 for (l = surf->poly_numverts - 1, k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;l = k, k++, v0 = v1, v1 += 3)
1324                                 {
1325                                         if (!surf->neighborsurfaces[l] || surf->neighborsurfaces[l]->castshadow != castshadowcount)
1326                                         {
1327                                                 VectorCopy(v1, &verts[0]);
1328                                                 VectorCopy(v0, &verts[3]);
1329                                                 VectorCopy(v0, &verts[6]);
1330                                                 VectorCopy(v1, &verts[9]);
1331                                                 VectorSubtract(&verts[6], e->origin, temp);
1332                                                 VectorNormalize(temp);
1333                                                 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1334                                                 VectorSubtract(&verts[9], e->origin, temp);
1335                                                 VectorNormalize(temp);
1336                                                 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1337                                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1338                                         }
1339                                 }
1340                         }
1341                         // build the triangle mesh
1342                         e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
1343                         {
1344                                 shadowmesh_t *mesh;
1345                                 l = 0;
1346                                 for (mesh = e->shadowvolume;mesh;mesh = mesh->next)
1347                                         l += mesh->numtriangles;
1348                                 Con_Printf("light %i shadow volume built containing %i triangles\n", lnum, l);
1349                         }
1350                 }
1351         }
1352 }
1353 */
1354
1355
1356 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1357 {
1358         loadmodel->brushq1.num_compressedpvs = 0;
1359         loadmodel->brushq1.data_compressedpvs = NULL;
1360         if (!l->filelen)
1361                 return;
1362         loadmodel->brushq1.num_compressedpvs = l->filelen;
1363         loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1364         memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1365 }
1366
1367 // used only for HalfLife maps
1368 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1369 {
1370         char key[128], value[4096];
1371         char wadname[128];
1372         int i, j, k;
1373         if (!data)
1374                 return;
1375         if (!COM_ParseToken(&data, false))
1376                 return; // error
1377         if (com_token[0] != '{')
1378                 return; // error
1379         while (1)
1380         {
1381                 if (!COM_ParseToken(&data, false))
1382                         return; // error
1383                 if (com_token[0] == '}')
1384                         break; // end of worldspawn
1385                 if (com_token[0] == '_')
1386                         strcpy(key, com_token + 1);
1387                 else
1388                         strcpy(key, com_token);
1389                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1390                         key[strlen(key)-1] = 0;
1391                 if (!COM_ParseToken(&data, false))
1392                         return; // error
1393                 strcpy(value, com_token);
1394                 if (!strcmp("wad", key)) // for HalfLife maps
1395                 {
1396                         if (loadmodel->brushq1.ishlbsp)
1397                         {
1398                                 j = 0;
1399                                 for (i = 0;i < 4096;i++)
1400                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1401                                                 break;
1402                                 if (value[i])
1403                                 {
1404                                         for (;i < 4096;i++)
1405                                         {
1406                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1407                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1408                                                         j = i+1;
1409                                                 else if (value[i] == ';' || value[i] == 0)
1410                                                 {
1411                                                         k = value[i];
1412                                                         value[i] = 0;
1413                                                         strcpy(wadname, "textures/");
1414                                                         strcat(wadname, &value[j]);
1415                                                         W_LoadTextureWadFile(wadname, false);
1416                                                         j = i+1;
1417                                                         if (!k)
1418                                                                 break;
1419                                                 }
1420                                         }
1421                                 }
1422                         }
1423                 }
1424         }
1425 }
1426
1427 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1428 {
1429         loadmodel->brush.entities = NULL;
1430         if (!l->filelen)
1431                 return;
1432         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1433         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1434         if (loadmodel->brushq1.ishlbsp)
1435                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1436 }
1437
1438
1439 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1440 {
1441         dvertex_t       *in;
1442         mvertex_t       *out;
1443         int                     i, count;
1444
1445         in = (void *)(mod_base + l->fileofs);
1446         if (l->filelen % sizeof(*in))
1447                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1448         count = l->filelen / sizeof(*in);
1449         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1450
1451         loadmodel->brushq1.vertexes = out;
1452         loadmodel->brushq1.numvertexes = count;
1453
1454         for ( i=0 ; i<count ; i++, in++, out++)
1455         {
1456                 out->position[0] = LittleFloat(in->point[0]);
1457                 out->position[1] = LittleFloat(in->point[1]);
1458                 out->position[2] = LittleFloat(in->point[2]);
1459         }
1460 }
1461
1462 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1463 {
1464         dmodel_t        *in;
1465         dmodel_t        *out;
1466         int                     i, j, count;
1467
1468         in = (void *)(mod_base + l->fileofs);
1469         if (l->filelen % sizeof(*in))
1470                 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1471         count = l->filelen / sizeof(*in);
1472         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1473
1474         loadmodel->brushq1.submodels = out;
1475         loadmodel->brushq1.numsubmodels = count;
1476
1477         for ( i=0 ; i<count ; i++, in++, out++)
1478         {
1479                 for (j=0 ; j<3 ; j++)
1480                 {
1481                         // spread the mins / maxs by a pixel
1482                         out->mins[j] = LittleFloat(in->mins[j]) - 1;
1483                         out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1484                         out->origin[j] = LittleFloat(in->origin[j]);
1485                 }
1486                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1487                         out->headnode[j] = LittleLong(in->headnode[j]);
1488                 out->visleafs = LittleLong(in->visleafs);
1489                 out->firstface = LittleLong(in->firstface);
1490                 out->numfaces = LittleLong(in->numfaces);
1491         }
1492 }
1493
1494 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1495 {
1496         dedge_t *in;
1497         medge_t *out;
1498         int     i, count;
1499
1500         in = (void *)(mod_base + l->fileofs);
1501         if (l->filelen % sizeof(*in))
1502                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1503         count = l->filelen / sizeof(*in);
1504         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1505
1506         loadmodel->brushq1.edges = out;
1507         loadmodel->brushq1.numedges = count;
1508
1509         for ( i=0 ; i<count ; i++, in++, out++)
1510         {
1511                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1512                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1513         }
1514 }
1515
1516 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1517 {
1518         texinfo_t *in;
1519         mtexinfo_t *out;
1520         int i, j, k, count, miptex;
1521
1522         in = (void *)(mod_base + l->fileofs);
1523         if (l->filelen % sizeof(*in))
1524                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1525         count = l->filelen / sizeof(*in);
1526         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1527
1528         loadmodel->brushq1.texinfo = out;
1529         loadmodel->brushq1.numtexinfo = count;
1530
1531         for (i = 0;i < count;i++, in++, out++)
1532         {
1533                 for (k = 0;k < 2;k++)
1534                         for (j = 0;j < 4;j++)
1535                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1536
1537                 miptex = LittleLong(in->miptex);
1538                 out->flags = LittleLong(in->flags);
1539
1540                 out->texture = NULL;
1541                 if (loadmodel->brushq1.textures)
1542                 {
1543                         if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1544                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1545                         else
1546                                 out->texture = loadmodel->brushq1.textures + miptex;
1547                 }
1548                 if (out->flags & TEX_SPECIAL)
1549                 {
1550                         // if texture chosen is NULL or the shader needs a lightmap,
1551                         // force to notexture water shader
1552                         if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1553                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1554                 }
1555                 else
1556                 {
1557                         // if texture chosen is NULL, force to notexture
1558                         if (out->texture == NULL)
1559                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1560                 }
1561         }
1562 }
1563
1564 #if 0
1565 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1566 {
1567         int             i, j;
1568         float   *v;
1569
1570         mins[0] = mins[1] = mins[2] = 9999;
1571         maxs[0] = maxs[1] = maxs[2] = -9999;
1572         v = verts;
1573         for (i = 0;i < numverts;i++)
1574         {
1575                 for (j = 0;j < 3;j++, v++)
1576                 {
1577                         if (*v < mins[j])
1578                                 mins[j] = *v;
1579                         if (*v > maxs[j])
1580                                 maxs[j] = *v;
1581                 }
1582         }
1583 }
1584
1585 #define MAX_SUBDIVPOLYTRIANGLES 4096
1586 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1587
1588 static int subdivpolyverts, subdivpolytriangles;
1589 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1590 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1591
1592 static int subdivpolylookupvert(vec3_t v)
1593 {
1594         int i;
1595         for (i = 0;i < subdivpolyverts;i++)
1596                 if (subdivpolyvert[i][0] == v[0]
1597                  && subdivpolyvert[i][1] == v[1]
1598                  && subdivpolyvert[i][2] == v[2])
1599                         return i;
1600         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1601                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1602         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1603         return subdivpolyverts++;
1604 }
1605
1606 static void SubdividePolygon(int numverts, float *verts)
1607 {
1608         int             i, i1, i2, i3, f, b, c, p;
1609         vec3_t  mins, maxs, front[256], back[256];
1610         float   m, *pv, *cv, dist[256], frac;
1611
1612         if (numverts > 250)
1613                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1614
1615         BoundPoly(numverts, verts, mins, maxs);
1616
1617         for (i = 0;i < 3;i++)
1618         {
1619                 m = (mins[i] + maxs[i]) * 0.5;
1620                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1621                 if (maxs[i] - m < 8)
1622                         continue;
1623                 if (m - mins[i] < 8)
1624                         continue;
1625
1626                 // cut it
1627                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1628                         dist[c] = cv[i] - m;
1629
1630                 f = b = 0;
1631                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1632                 {
1633                         if (dist[p] >= 0)
1634                         {
1635                                 VectorCopy(pv, front[f]);
1636                                 f++;
1637                         }
1638                         if (dist[p] <= 0)
1639                         {
1640                                 VectorCopy(pv, back[b]);
1641                                 b++;
1642                         }
1643                         if (dist[p] == 0 || dist[c] == 0)
1644                                 continue;
1645                         if ((dist[p] > 0) != (dist[c] > 0) )
1646                         {
1647                                 // clip point
1648                                 frac = dist[p] / (dist[p] - dist[c]);
1649                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1650                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1651                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1652                                 f++;
1653                                 b++;
1654                         }
1655                 }
1656
1657                 SubdividePolygon(f, front[0]);
1658                 SubdividePolygon(b, back[0]);
1659                 return;
1660         }
1661
1662         i1 = subdivpolylookupvert(verts);
1663         i2 = subdivpolylookupvert(verts + 3);
1664         for (i = 2;i < numverts;i++)
1665         {
1666                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1667                 {
1668                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1669                         return;
1670                 }
1671
1672                 i3 = subdivpolylookupvert(verts + i * 3);
1673                 subdivpolyindex[subdivpolytriangles][0] = i1;
1674                 subdivpolyindex[subdivpolytriangles][1] = i2;
1675                 subdivpolyindex[subdivpolytriangles][2] = i3;
1676                 i2 = i3;
1677                 subdivpolytriangles++;
1678         }
1679 }
1680
1681 //Breaks a polygon up along axial 64 unit
1682 //boundaries so that turbulent and sky warps
1683 //can be done reasonably.
1684 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1685 {
1686         int i, j;
1687         surfvertex_t *v;
1688         surfmesh_t *mesh;
1689
1690         subdivpolytriangles = 0;
1691         subdivpolyverts = 0;
1692         SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1693         if (subdivpolytriangles < 1)
1694                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1695
1696         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1697         mesh->numverts = subdivpolyverts;
1698         mesh->numtriangles = subdivpolytriangles;
1699         mesh->vertex = (surfvertex_t *)(mesh + 1);
1700         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1701         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1702
1703         for (i = 0;i < mesh->numtriangles;i++)
1704                 for (j = 0;j < 3;j++)
1705                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1706
1707         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1708         {
1709                 VectorCopy(subdivpolyvert[i], v->v);
1710                 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1711                 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1712         }
1713 }
1714 #endif
1715
1716 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1717 {
1718         surfmesh_t *mesh;
1719         mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1720         mesh->numverts = numverts;
1721         mesh->numtriangles = numtriangles;
1722         mesh->vertex3f = (float *)(mesh + 1);
1723         mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1724         mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1725         mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1726         mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1727         mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1728         mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1729         mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1730         mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1731         mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1732         return mesh;
1733 }
1734
1735 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1736 {
1737         int i, lindex, j;
1738         float *vec, *vert, mins[3], maxs[3], val, *v;
1739         mtexinfo_t *tex;
1740
1741         // convert edges back to a normal polygon
1742         surf->poly_numverts = numedges;
1743         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1744         for (i = 0;i < numedges;i++)
1745         {
1746                 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1747                 if (lindex > 0)
1748                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1749                 else
1750                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1751                 VectorCopy(vec, vert);
1752                 vert += 3;
1753         }
1754
1755         // calculate polygon bounding box and center
1756         vert = surf->poly_verts;
1757         VectorCopy(vert, mins);
1758         VectorCopy(vert, maxs);
1759         vert += 3;
1760         for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1761         {
1762                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1763                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1764                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1765         }
1766         VectorCopy(mins, surf->poly_mins);
1767         VectorCopy(maxs, surf->poly_maxs);
1768         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1769         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1770         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1771
1772         // generate surface extents information
1773         tex = surf->texinfo;
1774         mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1775         mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1776         for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1777         {
1778                 for (j = 0;j < 2;j++)
1779                 {
1780                         val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1781                         if (mins[j] > val)
1782                                 mins[j] = val;
1783                         if (maxs[j] < val)
1784                                 maxs[j] = val;
1785                 }
1786         }
1787         for (i = 0;i < 2;i++)
1788         {
1789                 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1790                 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1791         }
1792 }
1793
1794 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1795 {
1796         dface_t *in;
1797         msurface_t *surf;
1798         int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1799         surfmesh_t *mesh;
1800         float s, t;
1801
1802         in = (void *)(mod_base + l->fileofs);
1803         if (l->filelen % sizeof(*in))
1804                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1805         count = l->filelen / sizeof(*in);
1806         loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1807
1808         loadmodel->brushq1.numsurfaces = count;
1809         loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1810         loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1811         loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1812
1813         for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, in++, surf++)
1814         {
1815                 surf->number = surfnum;
1816                 // FIXME: validate edges, texinfo, etc?
1817                 firstedge = LittleLong(in->firstedge);
1818                 numedges = LittleShort(in->numedges);
1819                 if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges)
1820                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1821                 i = LittleShort(in->texinfo);
1822                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1823                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1824                 surf->texinfo = loadmodel->brushq1.texinfo + i;
1825                 surf->flags = surf->texinfo->texture->flags;
1826
1827                 planenum = LittleShort(in->planenum);
1828                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1829                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1830
1831                 if (LittleShort(in->side))
1832                         surf->flags |= SURF_PLANEBACK;
1833
1834                 surf->plane = loadmodel->brushq1.planes + planenum;
1835
1836                 // clear lightmap (filled in later)
1837                 surf->lightmaptexture = NULL;
1838
1839                 // force lightmap upload on first time seeing the surface
1840                 surf->cached_dlight = true;
1841
1842                 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1843
1844                 ssize = (surf->extents[0] >> 4) + 1;
1845                 tsize = (surf->extents[1] >> 4) + 1;
1846
1847                 // lighting info
1848                 for (i = 0;i < MAXLIGHTMAPS;i++)
1849                         surf->styles[i] = in->styles[i];
1850                 i = LittleLong(in->lightofs);
1851                 if (i == -1)
1852                         surf->samples = NULL;
1853                 else if (loadmodel->brushq1.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1854                         surf->samples = loadmodel->brushq1.lightdata + i;
1855                 else // LordHavoc: white lighting (bsp version 29)
1856                         surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1857
1858                 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1859                 {
1860                         if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1861                                 Host_Error("Bad surface extents");
1862                         // stainmap for permanent marks on walls
1863                         surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1864                         // clear to white
1865                         memset(surf->stainsamples, 255, ssize * tsize * 3);
1866                 }
1867         }
1868
1869         loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1870         loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1871
1872         for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++)
1873         {
1874                 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1875                 mesh->numverts = surf->poly_numverts;
1876                 mesh->numtriangles = surf->poly_numverts - 2;
1877                 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1878                 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1879                 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1880                 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1881                 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1882                 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1883                 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1884                 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1885                 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1886                 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1887
1888                 surf->lightmaptexturestride = 0;
1889                 surf->lightmaptexture = NULL;
1890
1891                 for (i = 0;i < mesh->numverts;i++)
1892                 {
1893                         mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1894                         mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1895                         mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1896                         s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1897                         t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1898                         mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1899                         mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1900                         mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1901                         mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1902                         mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1903                         mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1904                         mesh->lightmapoffsets[i] = 0;
1905                 }
1906
1907                 for (i = 0;i < mesh->numtriangles;i++)
1908                 {
1909                         mesh->element3i[i * 3 + 0] = 0;
1910                         mesh->element3i[i * 3 + 1] = i + 1;
1911                         mesh->element3i[i * 3 + 2] = i + 2;
1912                 }
1913
1914                 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1915                 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1916
1917                 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1918                 {
1919                         int i, iu, iv, smax, tmax;
1920                         float u, v, ubase, vbase, uscale, vscale;
1921
1922                         smax = surf->extents[0] >> 4;
1923                         tmax = surf->extents[1] >> 4;
1924
1925                         surf->flags |= SURF_LIGHTMAP;
1926                         if (r_miplightmaps.integer)
1927                         {
1928                                 surf->lightmaptexturestride = smax+1;
1929                                 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1930                         }
1931                         else
1932                         {
1933                                 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1934                                 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1935                         }
1936                         R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1937                         uscale = (uscale - ubase) / (smax + 1);
1938                         vscale = (vscale - vbase) / (tmax + 1);
1939
1940                         for (i = 0;i < mesh->numverts;i++)
1941                         {
1942                                 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1943                                 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1944                                 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1945                                 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1946                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1947                                 iu = (int) u;
1948                                 iv = (int) v;
1949                                 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1950                         }
1951                 }
1952         }
1953 }
1954
1955 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1956 {
1957         node->parent = parent;
1958         if (node->contents < 0)
1959                 return;
1960         Mod_Q1BSP_SetParent(node->children[0], node);
1961         Mod_Q1BSP_SetParent(node->children[1], node);
1962 }
1963
1964 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1965 {
1966         int                     i, j, count, p;
1967         dnode_t         *in;
1968         mnode_t         *out;
1969
1970         in = (void *)(mod_base + l->fileofs);
1971         if (l->filelen % sizeof(*in))
1972                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1973         count = l->filelen / sizeof(*in);
1974         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1975
1976         loadmodel->brushq1.nodes = out;
1977         loadmodel->brushq1.numnodes = count;
1978
1979         for ( i=0 ; i<count ; i++, in++, out++)
1980         {
1981                 for (j=0 ; j<3 ; j++)
1982                 {
1983                         out->mins[j] = LittleShort(in->mins[j]);
1984                         out->maxs[j] = LittleShort(in->maxs[j]);
1985                 }
1986
1987                 p = LittleLong(in->planenum);
1988                 out->plane = loadmodel->brushq1.planes + p;
1989
1990                 out->firstsurface = LittleShort(in->firstface);
1991                 out->numsurfaces = LittleShort(in->numfaces);
1992
1993                 for (j=0 ; j<2 ; j++)
1994                 {
1995                         p = LittleShort(in->children[j]);
1996                         if (p >= 0)
1997                                 out->children[j] = loadmodel->brushq1.nodes + p;
1998                         else
1999                                 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
2000                 }
2001         }
2002
2003         Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL);    // sets nodes and leafs
2004 }
2005
2006 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
2007 {
2008         dleaf_t *in;
2009         mleaf_t *out;
2010         int i, j, count, p, pvschainbytes;
2011         qbyte *pvs;
2012
2013         in = (void *)(mod_base + l->fileofs);
2014         if (l->filelen % sizeof(*in))
2015                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2016         count = l->filelen / sizeof(*in);
2017         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2018
2019         loadmodel->brushq1.leafs = out;
2020         loadmodel->brushq1.numleafs = count;
2021         pvschainbytes = ((loadmodel->brushq1.num_leafs - 1)+7)>>3;
2022         loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
2023
2024         for ( i=0 ; i<count ; i++, in++, out++)
2025         {
2026                 for (j=0 ; j<3 ; j++)
2027                 {
2028                         out->mins[j] = LittleShort(in->mins[j]);
2029                         out->maxs[j] = LittleShort(in->maxs[j]);
2030                 }
2031
2032                 // FIXME: this function could really benefit from some error checking
2033
2034                 out->contents = LittleLong(in->contents);
2035
2036                 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
2037                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
2038
2039                 out->pvsdata = pvs;
2040                 pvs += pvschainbytes;
2041
2042                 p = LittleLong(in->visofs);
2043                 if (p >= 0)
2044                         Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
2045                 else
2046                         memset(out->pvsdata, 0xFF, pvschainbytes);
2047
2048                 for (j = 0;j < 4;j++)
2049                         out->ambient_sound_level[j] = in->ambient_level[j];
2050
2051                 // FIXME: Insert caustics here
2052         }
2053 }
2054
2055 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
2056 {
2057         dclipnode_t *in, *out;
2058         int                     i, count;
2059         hull_t          *hull;
2060
2061         in = (void *)(mod_base + l->fileofs);
2062         if (l->filelen % sizeof(*in))
2063                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2064         count = l->filelen / sizeof(*in);
2065         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2066
2067         loadmodel->brushq1.clipnodes = out;
2068         loadmodel->brushq1.numclipnodes = count;
2069
2070         if (loadmodel->brushq1.ishlbsp)
2071         {
2072                 hull = &loadmodel->brushq1.hulls[1];
2073                 hull->clipnodes = out;
2074                 hull->firstclipnode = 0;
2075                 hull->lastclipnode = count-1;
2076                 hull->planes = loadmodel->brushq1.planes;
2077                 hull->clip_mins[0] = -16;
2078                 hull->clip_mins[1] = -16;
2079                 hull->clip_mins[2] = -36;
2080                 hull->clip_maxs[0] = 16;
2081                 hull->clip_maxs[1] = 16;
2082                 hull->clip_maxs[2] = 36;
2083                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2084
2085                 hull = &loadmodel->brushq1.hulls[2];
2086                 hull->clipnodes = out;
2087                 hull->firstclipnode = 0;
2088                 hull->lastclipnode = count-1;
2089                 hull->planes = loadmodel->brushq1.planes;
2090                 hull->clip_mins[0] = -32;
2091                 hull->clip_mins[1] = -32;
2092                 hull->clip_mins[2] = -32;
2093                 hull->clip_maxs[0] = 32;
2094                 hull->clip_maxs[1] = 32;
2095                 hull->clip_maxs[2] = 32;
2096                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2097
2098                 hull = &loadmodel->brushq1.hulls[3];
2099                 hull->clipnodes = out;
2100                 hull->firstclipnode = 0;
2101                 hull->lastclipnode = count-1;
2102                 hull->planes = loadmodel->brushq1.planes;
2103                 hull->clip_mins[0] = -16;
2104                 hull->clip_mins[1] = -16;
2105                 hull->clip_mins[2] = -18;
2106                 hull->clip_maxs[0] = 16;
2107                 hull->clip_maxs[1] = 16;
2108                 hull->clip_maxs[2] = 18;
2109                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2110         }
2111         else
2112         {
2113                 hull = &loadmodel->brushq1.hulls[1];
2114                 hull->clipnodes = out;
2115                 hull->firstclipnode = 0;
2116                 hull->lastclipnode = count-1;
2117                 hull->planes = loadmodel->brushq1.planes;
2118                 hull->clip_mins[0] = -16;
2119                 hull->clip_mins[1] = -16;
2120                 hull->clip_mins[2] = -24;
2121                 hull->clip_maxs[0] = 16;
2122                 hull->clip_maxs[1] = 16;
2123                 hull->clip_maxs[2] = 32;
2124                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2125
2126                 hull = &loadmodel->brushq1.hulls[2];
2127                 hull->clipnodes = out;
2128                 hull->firstclipnode = 0;
2129                 hull->lastclipnode = count-1;
2130                 hull->planes = loadmodel->brushq1.planes;
2131                 hull->clip_mins[0] = -32;
2132                 hull->clip_mins[1] = -32;
2133                 hull->clip_mins[2] = -24;
2134                 hull->clip_maxs[0] = 32;
2135                 hull->clip_maxs[1] = 32;
2136                 hull->clip_maxs[2] = 64;
2137                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2138         }
2139
2140         for (i=0 ; i<count ; i++, out++, in++)
2141         {
2142                 out->planenum = LittleLong(in->planenum);
2143                 out->children[0] = LittleShort(in->children[0]);
2144                 out->children[1] = LittleShort(in->children[1]);
2145                 if (out->children[0] >= count || out->children[1] >= count)
2146                         Host_Error("Corrupt clipping hull(out of range child)\n");
2147         }
2148 }
2149
2150 //Duplicate the drawing hull structure as a clipping hull
2151 static void Mod_Q1BSP_MakeHull0(void)
2152 {
2153         mnode_t         *in;
2154         dclipnode_t *out;
2155         int                     i;
2156         hull_t          *hull;
2157
2158         hull = &loadmodel->brushq1.hulls[0];
2159
2160         in = loadmodel->brushq1.nodes;
2161         out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
2162
2163         hull->clipnodes = out;
2164         hull->firstclipnode = 0;
2165         hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
2166         hull->planes = loadmodel->brushq1.planes;
2167
2168         for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
2169         {
2170                 out->planenum = in->plane - loadmodel->brushq1.planes;
2171                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
2172                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
2173         }
2174 }
2175
2176 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
2177 {
2178         int i, j;
2179         short *in;
2180
2181         in = (void *)(mod_base + l->fileofs);
2182         if (l->filelen % sizeof(*in))
2183                 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2184         loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2185         loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2186
2187         for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2188         {
2189                 j = (unsigned) LittleShort(in[i]);
2190                 if (j >= loadmodel->brushq1.numsurfaces)
2191                         Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2192                 loadmodel->brushq1.marksurfaces[i] = j;
2193         }
2194 }
2195
2196 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2197 {
2198         int             i;
2199         int             *in;
2200
2201         in = (void *)(mod_base + l->fileofs);
2202         if (l->filelen % sizeof(*in))
2203                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2204         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2205         loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2206
2207         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2208                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2209 }
2210
2211
2212 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2213 {
2214         int                     i;
2215         mplane_t        *out;
2216         dplane_t        *in;
2217
2218         in = (void *)(mod_base + l->fileofs);
2219         if (l->filelen % sizeof(*in))
2220                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2221
2222         loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2223         loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2224
2225         for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2226         {
2227                 out->normal[0] = LittleFloat(in->normal[0]);
2228                 out->normal[1] = LittleFloat(in->normal[1]);
2229                 out->normal[2] = LittleFloat(in->normal[2]);
2230                 out->dist = LittleFloat(in->dist);
2231
2232                 PlaneClassify(out);
2233         }
2234 }
2235
2236 #define MAX_POINTS_ON_WINDING 64
2237
2238 typedef struct
2239 {
2240         int numpoints;
2241         int padding;
2242         double points[8][3]; // variable sized
2243 }
2244 winding_t;
2245
2246 /*
2247 ==================
2248 NewWinding
2249 ==================
2250 */
2251 static winding_t *NewWinding(int points)
2252 {
2253         winding_t *w;
2254         int size;
2255
2256         if (points > MAX_POINTS_ON_WINDING)
2257                 Sys_Error("NewWinding: too many points\n");
2258
2259         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
2260         w = Mem_Alloc(loadmodel->mempool, size);
2261         memset(w, 0, size);
2262
2263         return w;
2264 }
2265
2266 static void FreeWinding(winding_t *w)
2267 {
2268         Mem_Free(w);
2269 }
2270
2271 /*
2272 =================
2273 BaseWindingForPlane
2274 =================
2275 */
2276 static winding_t *BaseWindingForPlane(mplane_t *p)
2277 {
2278         double org[3], vright[3], vup[3], normal[3];
2279         winding_t *w;
2280
2281         VectorCopy(p->normal, normal);
2282         VectorVectorsDouble(normal, vright, vup);
2283
2284         VectorScale(vup, 1024.0*1024.0*1024.0, vup);
2285         VectorScale(vright, 1024.0*1024.0*1024.0, vright);
2286
2287         // project a really big axis aligned box onto the plane
2288         w = NewWinding(4);
2289
2290         VectorScale(p->normal, p->dist, org);
2291
2292         VectorSubtract(org, vright, w->points[0]);
2293         VectorAdd(w->points[0], vup, w->points[0]);
2294
2295         VectorAdd(org, vright, w->points[1]);
2296         VectorAdd(w->points[1], vup, w->points[1]);
2297
2298         VectorAdd(org, vright, w->points[2]);
2299         VectorSubtract(w->points[2], vup, w->points[2]);
2300
2301         VectorSubtract(org, vright, w->points[3]);
2302         VectorSubtract(w->points[3], vup, w->points[3]);
2303
2304         w->numpoints = 4;
2305
2306         return w;
2307 }
2308
2309 /*
2310 ==================
2311 ClipWinding
2312
2313 Clips the winding to the plane, returning the new winding on the positive side
2314 Frees the input winding.
2315 If keepon is true, an exactly on-plane winding will be saved, otherwise
2316 it will be clipped away.
2317 ==================
2318 */
2319 static winding_t *ClipWinding(winding_t *in, mplane_t *split, int keepon)
2320 {
2321         double  dists[MAX_POINTS_ON_WINDING + 1];
2322         int             sides[MAX_POINTS_ON_WINDING + 1];
2323         int             counts[3];
2324         double  dot;
2325         int             i, j;
2326         double  *p1, *p2;
2327         double  mid[3];
2328         winding_t       *neww;
2329         int             maxpts;
2330
2331         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2332
2333         // determine sides for each point
2334         for (i = 0;i < in->numpoints;i++)
2335         {
2336                 dists[i] = dot = DotProduct(in->points[i], split->normal) - split->dist;
2337                 if (dot > ON_EPSILON)
2338                         sides[i] = SIDE_FRONT;
2339                 else if (dot < -ON_EPSILON)
2340                         sides[i] = SIDE_BACK;
2341                 else
2342                         sides[i] = SIDE_ON;
2343                 counts[sides[i]]++;
2344         }
2345         sides[i] = sides[0];
2346         dists[i] = dists[0];
2347
2348         if (keepon && !counts[0] && !counts[1])
2349                 return in;
2350
2351         if (!counts[0])
2352         {
2353                 FreeWinding(in);
2354                 return NULL;
2355         }
2356         if (!counts[1])
2357                 return in;
2358
2359         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2360         if (maxpts > MAX_POINTS_ON_WINDING)
2361                 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2362
2363         neww = NewWinding(maxpts);
2364
2365         for (i = 0;i < in->numpoints;i++)
2366         {
2367                 if (neww->numpoints >= maxpts)
2368                         Sys_Error("ClipWinding: points exceeded estimate");
2369
2370                 p1 = in->points[i];
2371
2372                 if (sides[i] == SIDE_ON)
2373                 {
2374                         VectorCopy(p1, neww->points[neww->numpoints]);
2375                         neww->numpoints++;
2376                         continue;
2377                 }
2378
2379                 if (sides[i] == SIDE_FRONT)
2380                 {
2381                         VectorCopy(p1, neww->points[neww->numpoints]);
2382                         neww->numpoints++;
2383                 }
2384
2385                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2386                         continue;
2387
2388                 // generate a split point
2389                 p2 = in->points[(i+1)%in->numpoints];
2390
2391                 dot = dists[i] / (dists[i]-dists[i+1]);
2392                 for (j = 0;j < 3;j++)
2393                 {       // avoid round off error when possible
2394                         if (split->normal[j] == 1)
2395                                 mid[j] = split->dist;
2396                         else if (split->normal[j] == -1)
2397                                 mid[j] = -split->dist;
2398                         else
2399                                 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2400                 }
2401
2402                 VectorCopy(mid, neww->points[neww->numpoints]);
2403                 neww->numpoints++;
2404         }
2405
2406         // free the original winding
2407         FreeWinding(in);
2408
2409         return neww;
2410 }
2411
2412
2413 /*
2414 ==================
2415 DivideWinding
2416
2417 Divides a winding by a plane, producing one or two windings.  The
2418 original winding is not damaged or freed.  If only on one side, the
2419 returned winding will be the input winding.  If on both sides, two
2420 new windings will be created.
2421 ==================
2422 */
2423 static void DivideWinding(winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2424 {
2425         double  dists[MAX_POINTS_ON_WINDING + 1];
2426         int             sides[MAX_POINTS_ON_WINDING + 1];
2427         int             counts[3];
2428         double  dot;
2429         int             i, j;
2430         double  *p1, *p2;
2431         double  mid[3];
2432         winding_t       *f, *b;
2433         int             maxpts;
2434
2435         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2436
2437         // determine sides for each point
2438         for (i = 0;i < in->numpoints;i++)
2439         {
2440                 dot = DotProduct(in->points[i], split->normal);
2441                 dot -= split->dist;
2442                 dists[i] = dot;
2443                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2444                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2445                 else sides[i] = SIDE_ON;
2446                 counts[sides[i]]++;
2447         }
2448         sides[i] = sides[0];
2449         dists[i] = dists[0];
2450
2451         *front = *back = NULL;
2452
2453         if (!counts[0])
2454         {
2455                 *back = in;
2456                 return;
2457         }
2458         if (!counts[1])
2459         {
2460                 *front = in;
2461                 return;
2462         }
2463
2464         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2465
2466         if (maxpts > MAX_POINTS_ON_WINDING)
2467                 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2468
2469         *front = f = NewWinding(maxpts);
2470         *back = b = NewWinding(maxpts);
2471
2472         for (i = 0;i < in->numpoints;i++)
2473         {
2474                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2475                         Sys_Error("DivideWinding: points exceeded estimate");
2476
2477                 p1 = in->points[i];
2478
2479                 if (sides[i] == SIDE_ON)
2480                 {
2481                         VectorCopy(p1, f->points[f->numpoints]);
2482                         f->numpoints++;
2483                         VectorCopy(p1, b->points[b->numpoints]);
2484                         b->numpoints++;
2485                         continue;
2486                 }
2487
2488                 if (sides[i] == SIDE_FRONT)
2489                 {
2490                         VectorCopy(p1, f->points[f->numpoints]);
2491                         f->numpoints++;
2492                 }
2493                 else if (sides[i] == SIDE_BACK)
2494                 {
2495                         VectorCopy(p1, b->points[b->numpoints]);
2496                         b->numpoints++;
2497                 }
2498
2499                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2500                         continue;
2501
2502                 // generate a split point
2503                 p2 = in->points[(i+1)%in->numpoints];
2504
2505                 dot = dists[i] / (dists[i]-dists[i+1]);
2506                 for (j = 0;j < 3;j++)
2507                 {       // avoid round off error when possible
2508                         if (split->normal[j] == 1)
2509                                 mid[j] = split->dist;
2510                         else if (split->normal[j] == -1)
2511                                 mid[j] = -split->dist;
2512                         else
2513                                 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2514                 }
2515
2516                 VectorCopy(mid, f->points[f->numpoints]);
2517                 f->numpoints++;
2518                 VectorCopy(mid, b->points[b->numpoints]);
2519                 b->numpoints++;
2520         }
2521 }
2522
2523 typedef struct portal_s
2524 {
2525         mplane_t plane;
2526         mnode_t *nodes[2];              // [0] = front side of plane
2527         struct portal_s *next[2];
2528         winding_t *winding;
2529         struct portal_s *chain; // all portals are linked into a list
2530 }
2531 portal_t;
2532
2533 static portal_t *portalchain;
2534
2535 /*
2536 ===========
2537 AllocPortal
2538 ===========
2539 */
2540 static portal_t *AllocPortal(void)
2541 {
2542         portal_t *p;
2543         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2544         p->chain = portalchain;
2545         portalchain = p;
2546         return p;
2547 }
2548
2549 static void FreePortal(portal_t *p)
2550 {
2551         Mem_Free(p);
2552 }
2553
2554 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2555 {
2556         // calculate children first
2557         if (node->children[0]->contents >= 0)
2558                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2559         if (node->children[1]->contents >= 0)
2560                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2561
2562         // make combined bounding box from children
2563         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2564         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2565         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2566         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2567         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2568         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2569 }
2570
2571 static void Mod_Q1BSP_FinalizePortals(void)
2572 {
2573         int i, j, numportals, numpoints;
2574         portal_t *p, *pnext;
2575         mportal_t *portal;
2576         mvertex_t *point;
2577         mleaf_t *leaf, *endleaf;
2578         winding_t *w;
2579
2580         // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2581         leaf = loadmodel->brushq1.leafs;
2582         endleaf = leaf + loadmodel->brushq1.numleafs;
2583         for (;leaf < endleaf;leaf++)
2584         {
2585                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2586                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2587         }
2588         p = portalchain;
2589         while (p)
2590         {
2591                 if (p->winding)
2592                 {
2593                         for (i = 0;i < 2;i++)
2594                         {
2595                                 leaf = (mleaf_t *)p->nodes[i];
2596                                 w = p->winding;
2597                                 for (j = 0;j < w->numpoints;j++)
2598                                 {
2599                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2600                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2601                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2602                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2603                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2604                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2605                                 }
2606                         }
2607                 }
2608                 p = p->chain;
2609         }
2610
2611         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2612
2613         // tally up portal and point counts
2614         p = portalchain;
2615         numportals = 0;
2616         numpoints = 0;
2617         while (p)
2618         {
2619                 // note: this check must match the one below or it will usually corrupt memory
2620                 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2621                 if (p->winding && p->nodes[0] != p->nodes[1]
2622                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2623                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2624                 {
2625                         numportals += 2;
2626                         numpoints += p->winding->numpoints * 2;
2627                 }
2628                 p = p->chain;
2629         }
2630         loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2631         loadmodel->brushq1.numportals = numportals;
2632         loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2633         loadmodel->brushq1.numportalpoints = numpoints;
2634         // clear all leaf portal chains
2635         for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2636                 loadmodel->brushq1.leafs[i].portals = NULL;
2637         // process all portals in the global portal chain, while freeing them
2638         portal = loadmodel->brushq1.portals;
2639         point = loadmodel->brushq1.portalpoints;
2640         p = portalchain;
2641         portalchain = NULL;
2642         while (p)
2643         {
2644                 pnext = p->chain;
2645
2646                 if (p->winding)
2647                 {
2648                         // note: this check must match the one above or it will usually corrupt memory
2649                         // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2650                         if (p->nodes[0] != p->nodes[1]
2651                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2652                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2653                         {
2654                                 // first make the back to front portal(forward portal)
2655                                 portal->points = point;
2656                                 portal->numpoints = p->winding->numpoints;
2657                                 portal->plane.dist = p->plane.dist;
2658                                 VectorCopy(p->plane.normal, portal->plane.normal);
2659                                 portal->here = (mleaf_t *)p->nodes[1];
2660                                 portal->past = (mleaf_t *)p->nodes[0];
2661                                 // copy points
2662                                 for (j = 0;j < portal->numpoints;j++)
2663                                 {
2664                                         VectorCopy(p->winding->points[j], point->position);
2665                                         point++;
2666                                 }
2667                                 PlaneClassify(&portal->plane);
2668
2669                                 // link into leaf's portal chain
2670                                 portal->next = portal->here->portals;
2671                                 portal->here->portals = portal;
2672
2673                                 // advance to next portal
2674                                 portal++;
2675
2676                                 // then make the front to back portal(backward portal)
2677                                 portal->points = point;
2678                                 portal->numpoints = p->winding->numpoints;
2679                                 portal->plane.dist = -p->plane.dist;
2680                                 VectorNegate(p->plane.normal, portal->plane.normal);
2681                                 portal->here = (mleaf_t *)p->nodes[0];
2682                                 portal->past = (mleaf_t *)p->nodes[1];
2683                                 // copy points
2684                                 for (j = portal->numpoints - 1;j >= 0;j--)
2685                                 {
2686                                         VectorCopy(p->winding->points[j], point->position);
2687                                         point++;
2688                                 }
2689                                 PlaneClassify(&portal->plane);
2690
2691                                 // link into leaf's portal chain
2692                                 portal->next = portal->here->portals;
2693                                 portal->here->portals = portal;
2694
2695                                 // advance to next portal
2696                                 portal++;
2697                         }
2698                         FreeWinding(p->winding);
2699                 }
2700                 FreePortal(p);
2701                 p = pnext;
2702         }
2703 }
2704
2705 /*
2706 =============
2707 AddPortalToNodes
2708 =============
2709 */
2710 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2711 {
2712         if (!front)
2713                 Host_Error("AddPortalToNodes: NULL front node");
2714         if (!back)
2715                 Host_Error("AddPortalToNodes: NULL back node");
2716         if (p->nodes[0] || p->nodes[1])
2717                 Host_Error("AddPortalToNodes: already included");
2718         // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
2719
2720         p->nodes[0] = front;
2721         p->next[0] = (portal_t *)front->portals;
2722         front->portals = (mportal_t *)p;
2723
2724         p->nodes[1] = back;
2725         p->next[1] = (portal_t *)back->portals;
2726         back->portals = (mportal_t *)p;
2727 }
2728
2729 /*
2730 =============
2731 RemovePortalFromNode
2732 =============
2733 */
2734 static void RemovePortalFromNodes(portal_t *portal)
2735 {
2736         int i;
2737         mnode_t *node;
2738         void **portalpointer;
2739         portal_t *t;
2740         for (i = 0;i < 2;i++)
2741         {
2742                 node = portal->nodes[i];
2743
2744                 portalpointer = (void **) &node->portals;
2745                 while (1)
2746                 {
2747                         t = *portalpointer;
2748                         if (!t)
2749                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2750
2751                         if (t == portal)
2752                         {
2753                                 if (portal->nodes[0] == node)
2754                                 {
2755                                         *portalpointer = portal->next[0];
2756                                         portal->nodes[0] = NULL;
2757                                 }
2758                                 else if (portal->nodes[1] == node)
2759                                 {
2760                                         *portalpointer = portal->next[1];
2761                                         portal->nodes[1] = NULL;
2762                                 }
2763                                 else
2764                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2765                                 break;
2766                         }
2767
2768                         if (t->nodes[0] == node)
2769                                 portalpointer = (void **) &t->next[0];
2770                         else if (t->nodes[1] == node)
2771                                 portalpointer = (void **) &t->next[1];
2772                         else
2773                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2774                 }
2775         }
2776 }
2777
2778 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2779 {
2780         int side;
2781         mnode_t *front, *back, *other_node;
2782         mplane_t clipplane, *plane;
2783         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2784         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2785
2786         // if a leaf, we're done
2787         if (node->contents)
2788                 return;
2789
2790         plane = node->plane;
2791
2792         front = node->children[0];
2793         back = node->children[1];
2794         if (front == back)
2795                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2796
2797         // create the new portal by generating a polygon for the node plane,
2798         // and clipping it by all of the other portals(which came from nodes above this one)
2799         nodeportal = AllocPortal();
2800         nodeportal->plane = *node->plane;
2801
2802         nodeportalwinding = BaseWindingForPlane(node->plane);
2803         side = 0;       // shut up compiler warning
2804         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2805         {
2806                 clipplane = portal->plane;
2807                 if (portal->nodes[0] == portal->nodes[1])
2808                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2809                 if (portal->nodes[0] == node)
2810                         side = 0;
2811                 else if (portal->nodes[1] == node)
2812                 {
2813                         clipplane.dist = -clipplane.dist;
2814                         VectorNegate(clipplane.normal, clipplane.normal);
2815                         side = 1;
2816                 }
2817                 else
2818                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2819
2820                 nodeportalwinding = ClipWinding(nodeportalwinding, &clipplane, true);
2821                 if (!nodeportalwinding)
2822                 {
2823                         Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2824                         break;
2825                 }
2826         }
2827
2828         if (nodeportalwinding)
2829         {
2830                 // if the plane was not clipped on all sides, there was an error
2831                 nodeportal->winding = nodeportalwinding;
2832                 AddPortalToNodes(nodeportal, front, back);
2833         }
2834
2835         // split the portals of this node along this node's plane and assign them to the children of this node
2836         // (migrating the portals downward through the tree)
2837         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2838         {
2839                 if (portal->nodes[0] == portal->nodes[1])
2840                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2841                 if (portal->nodes[0] == node)
2842                         side = 0;
2843                 else if (portal->nodes[1] == node)
2844                         side = 1;
2845                 else
2846                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2847                 nextportal = portal->next[side];
2848
2849                 other_node = portal->nodes[!side];
2850                 RemovePortalFromNodes(portal);
2851
2852                 // cut the portal into two portals, one on each side of the node plane
2853                 DivideWinding(portal->winding, plane, &frontwinding, &backwinding);
2854
2855                 if (!frontwinding)
2856                 {
2857                         if (side == 0)
2858                                 AddPortalToNodes(portal, back, other_node);
2859                         else
2860                                 AddPortalToNodes(portal, other_node, back);
2861                         continue;
2862                 }
2863                 if (!backwinding)
2864                 {
2865                         if (side == 0)
2866                                 AddPortalToNodes(portal, front, other_node);
2867                         else
2868                                 AddPortalToNodes(portal, other_node, front);
2869                         continue;
2870                 }
2871
2872                 // the winding is split
2873                 splitportal = AllocPortal();
2874                 temp = splitportal->chain;
2875                 *splitportal = *portal;
2876                 splitportal->chain = temp;
2877                 splitportal->winding = backwinding;
2878                 FreeWinding(portal->winding);
2879                 portal->winding = frontwinding;
2880
2881                 if (side == 0)
2882                 {
2883                         AddPortalToNodes(portal, front, other_node);
2884                         AddPortalToNodes(splitportal, back, other_node);
2885                 }
2886                 else
2887                 {
2888                         AddPortalToNodes(portal, other_node, front);
2889                         AddPortalToNodes(splitportal, other_node, back);
2890                 }
2891         }
2892
2893         Mod_Q1BSP_RecursiveNodePortals(front);
2894         Mod_Q1BSP_RecursiveNodePortals(back);
2895 }
2896
2897
2898 static void Mod_Q1BSP_MakePortals(void)
2899 {
2900         portalchain = NULL;
2901         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2902         Mod_Q1BSP_FinalizePortals();
2903 }
2904
2905 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2906 {
2907 #if 0
2908         int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2909         msurface_t *surf, *s;
2910         float *v0, *v1, *v2, *v3;
2911         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2912                 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2913         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2914         {
2915                 for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
2916                 {
2917                         if (surf->neighborsurfaces[vertnum])
2918                                 continue;
2919                         surf->neighborsurfaces[vertnum] = NULL;
2920                         for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2921                         {
2922                                 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2923                                  || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2924                                  || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2925                                  || s == surf)
2926                                         continue;
2927                                 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2928                                         if (s->neighborsurfaces[vnum] == surf)
2929                                                 break;
2930                                 if (vnum < s->poly_numverts)
2931                                         continue;
2932                                 for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
2933                                 {
2934                                         if (s->neighborsurfaces[vnum] == NULL
2935                                          && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2936                                           || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2937                                         {
2938                                                 surf->neighborsurfaces[vertnum] = s;
2939                                                 s->neighborsurfaces[vnum] = surf;
2940                                                 break;
2941                                         }
2942                                 }
2943                                 if (vnum < s->poly_numverts)
2944                                         break;
2945                         }
2946                 }
2947         }
2948 #endif
2949 }
2950
2951 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2952 {
2953         int i, j, stylecounts[256], totalcount, remapstyles[256];
2954         msurface_t *surf;
2955         memset(stylecounts, 0, sizeof(stylecounts));
2956         for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2957         {
2958                 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2959                 for (j = 0;j < MAXLIGHTMAPS;j++)
2960                         stylecounts[surf->styles[j]]++;
2961         }
2962         totalcount = 0;
2963         model->brushq1.light_styles = 0;
2964         for (i = 0;i < 255;i++)
2965         {
2966                 if (stylecounts[i])
2967                 {
2968                         remapstyles[i] = model->brushq1.light_styles++;
2969                         totalcount += stylecounts[i] + 1;
2970                 }
2971         }
2972         if (!totalcount)
2973                 return;
2974         model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2975         model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2976         model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2977         model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2978         model->brushq1.light_styles = 0;
2979         for (i = 0;i < 255;i++)
2980                 if (stylecounts[i])
2981                         model->brushq1.light_style[model->brushq1.light_styles++] = i;
2982         j = 0;
2983         for (i = 0;i < model->brushq1.light_styles;i++)
2984         {
2985                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2986                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2987         }
2988         for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2989         {
2990                 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2991                 for (j = 0;j < MAXLIGHTMAPS;j++)
2992                         if (surf->styles[j] != 255)
2993                                 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2994         }
2995         j = 0;
2996         for (i = 0;i < model->brushq1.light_styles;i++)
2997         {
2998                 *model->brushq1.light_styleupdatechains[i] = NULL;
2999                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
3000                 j += stylecounts[model->brushq1.light_style[i]] + 1;
3001         }
3002 }
3003
3004 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
3005 {
3006         int i, j;
3007         for (i = 0;i < model->brushq1.numtextures;i++)
3008                 model->brushq1.pvstexturechainslength[i] = 0;
3009         for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
3010         {
3011                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
3012                 {
3013                         model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
3014                         model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
3015                 }
3016         }
3017         for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
3018         {
3019                 if (model->brushq1.pvstexturechainslength[i])
3020                 {
3021                         model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
3022                         j += model->brushq1.pvstexturechainslength[i] + 1;
3023                 }
3024                 else
3025                         model->brushq1.pvstexturechains[i] = NULL;
3026         }
3027         for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
3028                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
3029                         *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
3030         for (i = 0;i < model->brushq1.numtextures;i++)
3031         {
3032                 if (model->brushq1.pvstexturechainslength[i])
3033                 {
3034                         *model->brushq1.pvstexturechains[i] = NULL;
3035                         model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
3036                 }
3037         }
3038 }
3039
3040 void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
3041 {
3042         int i;
3043         mplane_t *plane;
3044         float d;
3045
3046         while (1)
3047         {
3048         // if this is a leaf, accumulate the pvs bits
3049                 if (node->contents < 0)
3050                 {
3051                         if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
3052                                 for (i = 0;i < pvsbytes;i++)
3053                                         pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
3054                         return;
3055                 }
3056
3057                 plane = node->plane;
3058                 d = DotProduct(org, plane->normal) - plane->dist;
3059                 if (d > radius)
3060                         node = node->children[0];
3061                 else if (d < -radius)
3062                         node = node->children[1];
3063                 else
3064                 {       // go down both
3065                         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
3066                         node = node->children[1];
3067                 }
3068         }
3069 }
3070
3071 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
3072 //of the given point.
3073 int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
3074 {
3075         int bytes = ((model->brushq1.num_leafs - 1) + 7) >> 3;
3076         bytes = min(bytes, pvsbufferlength);
3077         memset(pvsbuffer, 0, bytes);
3078         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, sv.worldmodel->brushq1.nodes);
3079         return bytes;
3080 }
3081
3082 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
3083 extern void R_Model_Brush_Draw(entity_render_t *ent);
3084 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
3085 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
3086 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
3087 {
3088         int i, j, k;
3089         dheader_t *header;
3090         dmodel_t *bm;
3091         mempool_t *mainmempool;
3092         char *loadname;
3093         model_t *originalloadmodel;
3094         float dist, modelyawradius, modelradius, *vec;
3095         msurface_t *surf;
3096         surfmesh_t *mesh;
3097
3098         mod->type = mod_brush;
3099
3100         header = (dheader_t *)buffer;
3101
3102         i = LittleLong(header->version);
3103         if (i != BSPVERSION && i != 30)
3104                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
3105         mod->brushq1.ishlbsp = i == 30;
3106
3107         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
3108         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
3109         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
3110         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
3111         mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
3112         mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
3113         mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
3114
3115         if (loadmodel->isworldmodel)
3116         {
3117                 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
3118                 // until we get a texture for it...
3119                 R_ResetQuakeSky();
3120         }
3121
3122 // swap all the lumps
3123         mod_base = (qbyte *)header;
3124
3125         for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
3126                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3127
3128 // load into heap
3129
3130         // store which lightmap format to use
3131         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3132
3133         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
3134         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
3135         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
3136         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
3137         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
3138         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
3139         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
3140         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
3141         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
3142         Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
3143         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
3144         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
3145         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
3146         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
3147         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
3148
3149         if (mod->brushq1.data_compressedpvs)
3150                 Mem_Free(mod->brushq1.data_compressedpvs);
3151         mod->brushq1.data_compressedpvs = NULL;
3152         mod->brushq1.num_compressedpvs = 0;
3153
3154         Mod_Q1BSP_MakeHull0();
3155         Mod_Q1BSP_MakePortals();
3156
3157         if (developer.integer)
3158                 Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.num_leafs - 1, loadmodel->brushq1.numportals);
3159
3160         mod->numframes = 2;             // regular and alternate animation
3161
3162         mainmempool = mod->mempool;
3163         loadname = mod->name;
3164
3165         Mod_Q1BSP_LoadLightList();
3166         originalloadmodel = loadmodel;
3167
3168 //
3169 // set up the submodels(FIXME: this is confusing)
3170 //
3171         for (i = 0;i < mod->brushq1.numsubmodels;i++)
3172         {
3173                 bm = &mod->brushq1.submodels[i];
3174
3175                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3176                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3177                 {
3178                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3179                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3180                 }
3181
3182                 mod->brushq1.firstmodelsurface = bm->firstface;
3183                 mod->brushq1.nummodelsurfaces = bm->numfaces;
3184
3185                 // this gets altered below if sky is used
3186                 mod->DrawSky = NULL;
3187                 mod->Draw = R_Model_Brush_Draw;
3188                 mod->DrawFakeShadow = NULL;
3189                 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
3190                 mod->DrawLight = R_Model_Brush_DrawLight;
3191                 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
3192                 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
3193                 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
3194                 Mod_Q1BSP_BuildPVSTextureChains(mod);
3195                 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
3196                 if (mod->brushq1.nummodelsurfaces)
3197                 {
3198                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3199                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3200                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3201                         modelyawradius = 0;
3202                         modelradius = 0;
3203                         for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
3204                         {
3205                                 // we only need to have a drawsky function if it is used(usually only on world model)
3206                                 if (surf->texinfo->texture->shader == &Cshader_sky)
3207                                         mod->DrawSky = R_Model_Brush_DrawSky;
3208                                 // LordHavoc: submodels always clip, even if water
3209                                 if (mod->brushq1.numsubmodels - 1)
3210                                         surf->flags |= SURF_SOLIDCLIP;
3211                                 // calculate bounding shapes
3212                                 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
3213                                 {
3214                                         for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
3215                                         {
3216                                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3217                                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3218                                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3219                                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3220                                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3221                                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3222                                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
3223                                                 if (modelyawradius < dist)
3224                                                         modelyawradius = dist;
3225                                                 dist += vec[2]*vec[2];
3226                                                 if (modelradius < dist)
3227                                                         modelradius = dist;
3228                                         }
3229                                 }
3230                         }
3231                         modelyawradius = sqrt(modelyawradius);
3232                         modelradius = sqrt(modelradius);
3233                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3234                         mod->yawmins[2] = mod->normalmins[2];
3235                         mod->yawmaxs[2] = mod->normalmaxs[2];
3236                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3237                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3238                         mod->radius = modelradius;
3239                         mod->radius2 = modelradius * modelradius;
3240                 }
3241                 else
3242                 {
3243                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3244                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
3245                 }
3246                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
3247
3248                 mod->brushq1.numleafs = bm->visleafs;
3249
3250                 // LordHavoc: only register submodels if it is the world
3251                 // (prevents bsp models from replacing world submodels)
3252                 if (loadmodel->isworldmodel && i < (mod->brushq1.numsubmodels - 1))
3253                 {
3254                         char    name[10];
3255                         // duplicate the basic information
3256                         sprintf(name, "*%i", i+1);
3257                         loadmodel = Mod_FindName(name);
3258                         *loadmodel = *mod;
3259                         strcpy(loadmodel->name, name);
3260                         // textures and memory belong to the main model
3261                         loadmodel->texturepool = NULL;
3262                         loadmodel->mempool = NULL;
3263                         mod = loadmodel;
3264                 }
3265         }
3266
3267         loadmodel = originalloadmodel;
3268         //Mod_Q1BSP_ProcessLightList();
3269 }
3270
3271 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3272 {
3273 }
3274
3275 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3276 {
3277 /*
3278         d_t *in;
3279         m_t *out;
3280         int i, count;
3281
3282         in = (void *)(mod_base + l->fileofs);
3283         if (l->filelen % sizeof(*in))
3284                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3285         count = l->filelen / sizeof(*in);
3286         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3287
3288         loadmodel-> = out;
3289         loadmodel->num = count;
3290
3291         for (i = 0;i < count;i++, in++, out++)
3292         {
3293         }
3294 */
3295 }
3296
3297 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3298 {
3299 /*
3300         d_t *in;
3301         m_t *out;
3302         int i, count;
3303
3304         in = (void *)(mod_base + l->fileofs);
3305         if (l->filelen % sizeof(*in))
3306                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3307         count = l->filelen / sizeof(*in);
3308         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3309
3310         loadmodel-> = out;
3311         loadmodel->num = count;
3312
3313         for (i = 0;i < count;i++, in++, out++)
3314         {
3315         }
3316 */
3317 }
3318
3319 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3320 {
3321 /*
3322         d_t *in;
3323         m_t *out;
3324         int i, count;
3325
3326         in = (void *)(mod_base + l->fileofs);
3327         if (l->filelen % sizeof(*in))
3328                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3329         count = l->filelen / sizeof(*in);
3330         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3331
3332         loadmodel-> = out;
3333         loadmodel->num = count;
3334
3335         for (i = 0;i < count;i++, in++, out++)
3336         {
3337         }
3338 */
3339 }
3340
3341 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3342 {
3343 /*
3344         d_t *in;
3345         m_t *out;
3346         int i, count;
3347
3348         in = (void *)(mod_base + l->fileofs);
3349         if (l->filelen % sizeof(*in))
3350                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3351         count = l->filelen / sizeof(*in);
3352         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3353
3354         loadmodel-> = out;
3355         loadmodel->num = count;
3356
3357         for (i = 0;i < count;i++, in++, out++)
3358         {
3359         }
3360 */
3361 }
3362
3363 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3364 {
3365 /*
3366         d_t *in;
3367         m_t *out;
3368         int i, count;
3369
3370         in = (void *)(mod_base + l->fileofs);
3371         if (l->filelen % sizeof(*in))
3372                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3373         count = l->filelen / sizeof(*in);
3374         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3375
3376         loadmodel-> = out;
3377         loadmodel->num = count;
3378
3379         for (i = 0;i < count;i++, in++, out++)
3380         {
3381         }
3382 */
3383 }
3384
3385 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3386 {
3387 /*
3388         d_t *in;
3389         m_t *out;
3390         int i, count;
3391
3392         in = (void *)(mod_base + l->fileofs);
3393         if (l->filelen % sizeof(*in))
3394                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3395         count = l->filelen / sizeof(*in);
3396         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3397
3398         loadmodel-> = out;
3399         loadmodel->num = count;
3400
3401         for (i = 0;i < count;i++, in++, out++)
3402         {
3403         }
3404 */
3405 }
3406
3407 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3408 {
3409 /*
3410         d_t *in;
3411         m_t *out;
3412         int i, count;
3413
3414         in = (void *)(mod_base + l->fileofs);
3415         if (l->filelen % sizeof(*in))
3416                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3417         count = l->filelen / sizeof(*in);
3418         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3419
3420         loadmodel-> = out;
3421         loadmodel->num = count;
3422
3423         for (i = 0;i < count;i++, in++, out++)
3424         {
3425         }
3426 */
3427 }
3428
3429 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3430 {
3431 /*
3432         d_t *in;
3433         m_t *out;
3434         int i, count;
3435
3436         in = (void *)(mod_base + l->fileofs);
3437         if (l->filelen % sizeof(*in))
3438                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3439         count = l->filelen / sizeof(*in);
3440         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3441
3442         loadmodel-> = out;
3443         loadmodel->num = count;
3444
3445         for (i = 0;i < count;i++, in++, out++)
3446         {
3447         }
3448 */
3449 }
3450
3451 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3452 {
3453 /*
3454         d_t *in;
3455         m_t *out;
3456         int i, count;
3457
3458         in = (void *)(mod_base + l->fileofs);
3459         if (l->filelen % sizeof(*in))
3460                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3461         count = l->filelen / sizeof(*in);
3462         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3463
3464         loadmodel-> = out;
3465         loadmodel->num = count;
3466
3467         for (i = 0;i < count;i++, in++, out++)
3468         {
3469         }
3470 */
3471 }
3472
3473 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3474 {
3475 /*
3476         d_t *in;
3477         m_t *out;
3478         int i, count;
3479
3480         in = (void *)(mod_base + l->fileofs);
3481         if (l->filelen % sizeof(*in))
3482                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3483         count = l->filelen / sizeof(*in);
3484         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3485
3486         loadmodel-> = out;
3487         loadmodel->num = count;
3488
3489         for (i = 0;i < count;i++, in++, out++)
3490         {
3491         }
3492 */
3493 }
3494
3495 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3496 {
3497 /*
3498         d_t *in;
3499         m_t *out;
3500         int i, count;
3501
3502         in = (void *)(mod_base + l->fileofs);
3503         if (l->filelen % sizeof(*in))
3504                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3505         count = l->filelen / sizeof(*in);
3506         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3507
3508         loadmodel-> = out;
3509         loadmodel->num = count;
3510
3511         for (i = 0;i < count;i++, in++, out++)
3512         {
3513         }
3514 */
3515 }
3516
3517 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3518 {
3519 /*
3520         d_t *in;
3521         m_t *out;
3522         int i, count;
3523
3524         in = (void *)(mod_base + l->fileofs);
3525         if (l->filelen % sizeof(*in))
3526                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3527         count = l->filelen / sizeof(*in);
3528         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3529
3530         loadmodel-> = out;
3531         loadmodel->num = count;
3532
3533         for (i = 0;i < count;i++, in++, out++)
3534         {
3535         }
3536 */
3537 }
3538
3539 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3540 {
3541 /*
3542         d_t *in;
3543         m_t *out;
3544         int i, count;
3545
3546         in = (void *)(mod_base + l->fileofs);
3547         if (l->filelen % sizeof(*in))
3548                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3549         count = l->filelen / sizeof(*in);
3550         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3551
3552         loadmodel-> = out;
3553         loadmodel->num = count;
3554
3555         for (i = 0;i < count;i++, in++, out++)
3556         {
3557         }
3558 */
3559 }
3560
3561 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3562 {
3563 /*
3564         d_t *in;
3565         m_t *out;
3566         int i, count;
3567
3568         in = (void *)(mod_base + l->fileofs);
3569         if (l->filelen % sizeof(*in))
3570                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3571         count = l->filelen / sizeof(*in);
3572         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3573
3574         loadmodel-> = out;
3575         loadmodel->num = count;
3576
3577         for (i = 0;i < count;i++, in++, out++)
3578         {
3579         }
3580 */
3581 }
3582
3583 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3584 {
3585 /*
3586         d_t *in;
3587         m_t *out;
3588         int i, count;
3589
3590         in = (void *)(mod_base + l->fileofs);
3591         if (l->filelen % sizeof(*in))
3592                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3593         count = l->filelen / sizeof(*in);
3594         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3595
3596         loadmodel-> = out;
3597         loadmodel->num = count;
3598
3599         for (i = 0;i < count;i++, in++, out++)
3600         {
3601         }
3602 */
3603 }
3604
3605 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3606 {
3607 /*
3608         d_t *in;
3609         m_t *out;
3610         int i, count;
3611
3612         in = (void *)(mod_base + l->fileofs);
3613         if (l->filelen % sizeof(*in))
3614                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3615         count = l->filelen / sizeof(*in);
3616         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3617
3618         loadmodel-> = out;
3619         loadmodel->num = count;
3620
3621         for (i = 0;i < count;i++, in++, out++)
3622         {
3623         }
3624 */
3625 }
3626
3627 static void Mod_Q2BSP_LoadModels(lump_t *l)
3628 {
3629 /*
3630         d_t *in;
3631         m_t *out;
3632         int i, count;
3633
3634         in = (void *)(mod_base + l->fileofs);
3635         if (l->filelen % sizeof(*in))
3636                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3637         count = l->filelen / sizeof(*in);
3638         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3639
3640         loadmodel-> = out;
3641         loadmodel->num = count;
3642
3643         for (i = 0;i < count;i++, in++, out++)
3644         {
3645         }
3646 */
3647 }
3648
3649 void Mod_Q2BSP_Load(model_t *mod, void *buffer)
3650 {
3651         int i;
3652         q2dheader_t *header;
3653
3654         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3655
3656         mod->type = mod_brushq2;
3657
3658         header = (q2dheader_t *)buffer;
3659
3660         i = LittleLong(header->version);
3661         if (i != Q2BSPVERSION)
3662                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3663         mod->brushq1.ishlbsp = false;
3664         if (loadmodel->isworldmodel)
3665         {
3666                 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
3667                 // until we get a texture for it...
3668                 R_ResetQuakeSky();
3669         }
3670
3671         mod_base = (qbyte *)header;
3672
3673         // swap all the lumps
3674         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3675                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3676
3677         // store which lightmap format to use
3678         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3679
3680         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3681         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3682         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3683         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3684         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3685         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3686         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3687         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3688         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3689         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3690         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3691         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3692         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3693         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3694         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3695         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3696         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3697         // LordHavoc: must go last because this makes the submodels
3698         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3699 }
3700
3701
3702 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3703 {
3704         const char *data;
3705         char key[128], value[4096];
3706         float v[3];
3707         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3708         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3709         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3710         if (!l->filelen)
3711                 return;
3712         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3713         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3714         data = loadmodel->brush.entities;
3715         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3716         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3717         {
3718                 while (1)
3719                 {
3720                         if (!COM_ParseToken(&data, false))
3721                                 break; // error
3722                         if (com_token[0] == '}')
3723                                 break; // end of worldspawn
3724                         if (com_token[0] == '_')
3725                                 strcpy(key, com_token + 1);
3726                         else
3727                                 strcpy(key, com_token);
3728                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3729                                 key[strlen(key)-1] = 0;
3730                         if (!COM_ParseToken(&data, false))
3731                                 break; // error
3732                         strcpy(value, com_token);
3733                         if (!strcmp("gridsize", key))
3734                         {
3735                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3736                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3737                         }
3738                 }
3739         }
3740 }
3741
3742 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3743 {
3744         q3dtexture_t *in;
3745         q3mtexture_t *out;
3746         int i, count;
3747
3748         in = (void *)(mod_base + l->fileofs);
3749         if (l->filelen % sizeof(*in))
3750                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3751         count = l->filelen / sizeof(*in);
3752         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3753
3754         loadmodel->brushq3.data_textures = out;
3755         loadmodel->brushq3.num_textures = count;
3756
3757         for (i = 0;i < count;i++, in++, out++)
3758         {
3759                 strncpy(out->name, in->name, sizeof(out->name) - 1);
3760                 out->surfaceflags = LittleLong(in->surfaceflags);
3761                 out->contents = LittleLong(in->contents);
3762
3763                 out->number = i;
3764                 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3765         }
3766 }
3767
3768 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3769 {
3770         q3dplane_t *in;
3771         mplane_t *out;
3772         int i, count;
3773
3774         in = (void *)(mod_base + l->fileofs);
3775         if (l->filelen % sizeof(*in))
3776                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3777         count = l->filelen / sizeof(*in);
3778         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3779
3780         loadmodel->brushq3.data_planes = out;
3781         loadmodel->brushq3.num_planes = count;
3782
3783         for (i = 0;i < count;i++, in++, out++)
3784         {
3785                 out->normal[0] = LittleLong(in->normal[0]);
3786                 out->normal[1] = LittleLong(in->normal[1]);
3787                 out->normal[2] = LittleLong(in->normal[2]);
3788                 out->dist = LittleLong(in->dist);
3789                 PlaneClassify(out);
3790         }
3791 }
3792
3793 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3794 {
3795         q3dbrushside_t *in;
3796         q3mbrushside_t *out;
3797         int i, n, count;
3798
3799         in = (void *)(mod_base + l->fileofs);
3800         if (l->filelen % sizeof(*in))
3801                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3802         count = l->filelen / sizeof(*in);
3803         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3804
3805         loadmodel->brushq3.data_brushsides = out;
3806         loadmodel->brushq3.num_brushsides = count;
3807
3808         for (i = 0;i < count;i++, in++, out++)
3809         {
3810                 n = LittleLong(in->planeindex);
3811                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3812                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3813                 out->plane = loadmodel->brushq3.data_planes + n;
3814                 n = LittleLong(in->textureindex);
3815                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3816                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3817                 out->texture = loadmodel->brushq3.data_textures + n;
3818         }
3819 }
3820
3821 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3822 {
3823         q3dbrush_t *in;
3824         q3mbrush_t *out;
3825         int i, j, k, m, n, c, count, numpoints, numplanes;
3826         winding_t *w;
3827         colpointf_t pointsbuf[256*3];
3828         colplanef_t planesbuf[256], colplanef;
3829
3830         in = (void *)(mod_base + l->fileofs);
3831         if (l->filelen % sizeof(*in))
3832                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3833         count = l->filelen / sizeof(*in);
3834         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3835
3836         loadmodel->brushq3.data_brushes = out;
3837         loadmodel->brushq3.num_brushes = count;
3838
3839         for (i = 0;i < count;i++, in++, out++)
3840         {
3841                 n = LittleLong(in->firstbrushside);
3842                 c = LittleLong(in->numbrushsides);
3843                 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3844                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3845                 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3846                 out->numbrushsides = c;
3847                 n = LittleLong(in->textureindex);
3848                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3849                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3850                 out->texture = loadmodel->brushq3.data_textures + n;
3851
3852                 // construct a collision brush, which needs points and planes...
3853                 // each point and plane should be unique, and they don't refer to
3854                 // eachother in any way, so keeping them unique is fairly easy
3855                 numpoints = 0;
3856                 numplanes = 0;
3857                 for (j = 0;j < out->numbrushsides;j++)
3858                 {
3859                         // create a huge polygon for the plane
3860                         w = BaseWindingForPlane(out->firstbrushside[j].plane);
3861                         // clip it by all other planes
3862                         for (k = 0;k < out->numbrushsides && w;k++)
3863                                 if (k != j)
3864                                         w = ClipWinding(w, out->firstbrushside[k].plane, true);
3865                         // if nothing is left, skip it
3866                         // FIXME: should keep count of how many were skipped and report
3867                         // it, just for sake of statistics
3868                         if (!w)
3869                                 continue;
3870                         // add the points uniquely (no duplicates)
3871                         for (k = 0;k < w->numpoints;k++)
3872                         {
3873                                 for (m = 0;m < numpoints;m++)
3874                                         if (VectorDistance2(w->points[k * 3], pointsbuf[m * 3].v) < DIST_EPSILON)
3875                                                 break;
3876                                 if (m == numpoints)
3877                                 {
3878                                         // check if there are too many and skip the brush
3879                                         if (numpoints >= 256)
3880                                         {
3881                                                 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many points for buffer\n");
3882                                                 FreeWinding(w);
3883                                                 goto failedtomakecolbrush;
3884                                         }
3885                                         // add the new one
3886                                         VectorCopy(w->points[k * 3], pointsbuf[numpoints * 3].v);
3887                                         numpoints++;
3888                                 }
3889                         }
3890                         // add the plane uniquely (no duplicates)
3891                         memset(&colplanef, 0, sizeof(colplanef));
3892                         VectorCopy(out->firstbrushside[k].plane->normal, colplanef.normal);
3893                         colplanef.dist = out->firstbrushside[k].plane->dist;
3894                         for (k = 0;k < numplanes;k++)
3895                                 if (VectorCompare(planesbuf[k].normal, colplanef.normal) && planesbuf[k].dist == colplanef.dist)
3896                                         break;
3897                         if (k == numplanes)
3898                         {
3899                                 // check if there are too many and skip the brush
3900                                 if (numplanes >= 256)
3901                                 {
3902                                         Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many planes for buffer\n");
3903                                         FreeWinding(w);
3904                                         goto failedtomakecolbrush;
3905                                 }
3906                                 // add the new one
3907                                 planesbuf[numplanes++] = colplanef;
3908                         }
3909                         FreeWinding(w);
3910                 }
3911                 // if anything is left, create the collision brush
3912                 if (numplanes && numpoints)
3913                 {
3914                         out->colbrushf = Collision_AllocBrushFloat(loadmodel->mempool, numpoints, numplanes);
3915                         memcpy(out->colbrushf->points, pointsbuf, numpoints * sizeof(float[3]));
3916                         memcpy(out->colbrushf->planes, planesbuf, numplanes * sizeof(mplane_t));
3917                 }
3918                 // return from errors to here
3919                 failedtomakecolbrush:;
3920         }
3921 }
3922
3923 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3924 {
3925         q3deffect_t *in;
3926         q3meffect_t *out;
3927         int i, n, count;
3928
3929         in = (void *)(mod_base + l->fileofs);
3930         if (l->filelen % sizeof(*in))
3931                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3932         count = l->filelen / sizeof(*in);
3933         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3934
3935         loadmodel->brushq3.data_effects = out;
3936         loadmodel->brushq3.num_effects = count;
3937
3938         for (i = 0;i < count;i++, in++, out++)
3939         {
3940                 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3941                 n = LittleLong(in->brushindex);
3942                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3943                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3944                 out->brush = loadmodel->brushq3.data_brushes + n;
3945                 out->unknown = LittleLong(in->unknown);
3946         }
3947 }
3948
3949 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3950 {
3951         q3dvertex_t *in;
3952         int i, count;
3953
3954         in = (void *)(mod_base + l->fileofs);
3955         if (l->filelen % sizeof(*in))
3956                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3957         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3958         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3959         loadmodel->brushq3.data_texturetexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3960         loadmodel->brushq3.data_lightmaptexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3961         loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3962         loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3963         loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3964         loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3965
3966         for (i = 0;i < count;i++, in++)
3967         {
3968                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3969                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3970                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3971                 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3972                 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3973                 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3974                 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3975                 // svector/tvector are calculated later in face loading
3976                 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3977                 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3978                 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3979                 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3980                 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3981                 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3982                 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3983                 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3984                 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3985                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3986                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3987                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3988                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3989         }
3990 }
3991
3992 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3993 {
3994         int *in;
3995         int *out;
3996         int i, count;
3997
3998         in = (void *)(mod_base + l->fileofs);
3999         if (l->filelen % sizeof(int[3]))
4000                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
4001         count = l->filelen / sizeof(*in);
4002         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4003
4004         loadmodel->brushq3.num_triangles = count / 3;
4005         loadmodel->brushq3.data_element3i = out;
4006         loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4007
4008         for (i = 0;i < count;i++, in++, out++)
4009         {
4010                 *out = LittleLong(*in);
4011                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
4012                         Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices);
4013         }
4014 }
4015
4016 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
4017 {
4018         q3dlightmap_t *in;
4019         rtexture_t **out;
4020         int i, count;
4021
4022         in = (void *)(mod_base + l->fileofs);
4023         if (l->filelen % sizeof(*in))
4024                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4025         count = l->filelen / sizeof(*in);
4026         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4027
4028         loadmodel->brushq3.data_lightmaps = out;
4029         loadmodel->brushq3.num_lightmaps = count;
4030
4031         for (i = 0;i < count;i++, in++, out++)
4032                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4033 }
4034
4035 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4036 {
4037         q3dface_t *in;
4038         q3mface_t *out;
4039         int i, j, n, count, invalidelements, patchsize[2];
4040
4041         in = (void *)(mod_base + l->fileofs);
4042         if (l->filelen % sizeof(*in))
4043                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4044         count = l->filelen / sizeof(*in);
4045         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4046
4047         loadmodel->brushq3.data_faces = out;
4048         loadmodel->brushq3.num_faces = count;
4049
4050         for (i = 0;i < count;i++, in++, out++)
4051         {
4052                 // check face type first
4053                 out->type = LittleLong(in->type);
4054                 if (out->type != Q3FACETYPE_POLYGON
4055                  && out->type != Q3FACETYPE_PATCH
4056                  && out->type != Q3FACETYPE_MESH
4057                  && out->type != Q3FACETYPE_FLARE)
4058                 {
4059                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
4060                         out->type = 0; // error
4061                         continue;
4062                 }
4063
4064                 n = LittleLong(in->textureindex);
4065                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
4066                 {
4067                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
4068                         out->type = 0; // error
4069                         continue;
4070                         n = 0;
4071                 }
4072                 out->texture = loadmodel->brushq3.data_textures + n;
4073                 n = LittleLong(in->effectindex);
4074                 if (n < -1 || n >= loadmodel->brushq3.num_effects)
4075                 {
4076                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4077                         n = -1;
4078                 }
4079                 if (n == -1)
4080                         out->effect = NULL;
4081                 else
4082                         out->effect = loadmodel->brushq3.data_effects + n;
4083                 n = LittleLong(in->lightmapindex);
4084                 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
4085                 {
4086                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4087                         n = -1;
4088                 }
4089                 if (n == -1)
4090                         out->lightmaptexture = NULL;
4091                 else
4092                         out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4093
4094                 out->firstvertex = LittleLong(in->firstvertex);
4095                 out->numvertices = LittleLong(in->numvertices);
4096                 out->firstelement = LittleLong(in->firstelement);
4097                 out->numelements = LittleLong(in->numelements);
4098                 out->numtriangles = out->numelements / 3;
4099                 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
4100                 {
4101                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->numvertices, loadmodel->brushq3.num_vertices);
4102                         out->type = 0; // error
4103                         continue;
4104                 }
4105                 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
4106                 {
4107                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->numelements, loadmodel->brushq3.num_triangles * 3);
4108                         out->type = 0; // error
4109                         continue;
4110                 }
4111                 if (out->numtriangles * 3 != out->numelements)
4112                 {
4113                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
4114                         out->type = 0; // error
4115                         continue;
4116                 }
4117                 switch(out->type)
4118                 {
4119                 case Q3FACETYPE_POLYGON:
4120                 case Q3FACETYPE_MESH:
4121                         out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4122                         out->data_texturetexcoord2f = loadmodel->brushq3.data_texturetexcoord2f + out->firstvertex * 2;
4123                         out->data_lightmaptexcoord2f = loadmodel->brushq3.data_lightmaptexcoord2f + out->firstvertex * 2;
4124                         out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4125                         out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4126                         out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4127                         out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4128                         out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4129                         out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4130                         break;
4131                 case Q3FACETYPE_PATCH:
4132                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4133                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4134                         if (patchsize[0] < 1 || patchsize[1] < 1)
4135                         {
4136                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4137                                 out->type = 0; // error
4138                                 continue;
4139                         }
4140                         // FIXME: convert patch to triangles here!
4141                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
4142                         out->type = 0;
4143                         continue;
4144                         break;
4145                 case Q3FACETYPE_FLARE:
4146                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4147                         out->type = 0;
4148                         continue;
4149                         break;
4150                 }
4151                 for (j = 0, invalidelements = 0;j < out->numelements;j++)
4152                         if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
4153                                 invalidelements++;
4154                 if (invalidelements)
4155                 {
4156                         Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->contents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->contents, out->firstvertex, out->numvertices, out->firstelement, out->numelements);
4157                         for (j = 0;j < out->numelements;j++)
4158                         {
4159                                 Con_Printf(" %i", out->data_element3i[j]);
4160                                 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
4161                                         out->data_element3i[j] = 0;
4162                         }
4163                         Con_Printf("\n");
4164                 }
4165         }
4166 }
4167
4168 static void Mod_Q3BSP_LoadModels(lump_t *l)
4169 {
4170         q3dmodel_t *in;
4171         q3mmodel_t *out;
4172         int i, j, n, c, count;
4173
4174         in = (void *)(mod_base + l->fileofs);
4175         if (l->filelen % sizeof(*in))
4176                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4177         count = l->filelen / sizeof(*in);
4178         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4179
4180         loadmodel->brushq3.data_models = out;
4181         loadmodel->brushq3.num_models = count;
4182
4183         for (i = 0;i < count;i++, in++, out++)
4184         {
4185                 for (j = 0;j < 3;j++)
4186                 {
4187                         out->mins[j] = LittleFloat(in->mins[j]);
4188                         out->maxs[j] = LittleFloat(in->maxs[j]);
4189                 }
4190                 n = LittleLong(in->firstface);
4191                 c = LittleLong(in->numfaces);
4192                 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4193                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4194                 out->firstface = loadmodel->brushq3.data_faces + n;
4195                 out->numfaces = c;
4196                 n = LittleLong(in->firstbrush);
4197                 c = LittleLong(in->numbrushes);
4198                 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4199                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4200                 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4201                 out->numbrushes = c;
4202         }
4203 }
4204
4205 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4206 {
4207         int *in;
4208         q3mbrush_t **out;
4209         int i, n, count;
4210
4211         in = (void *)(mod_base + l->fileofs);
4212         if (l->filelen % sizeof(*in))
4213                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4214         count = l->filelen / sizeof(*in);
4215         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4216
4217         loadmodel->brushq3.data_leafbrushes = out;
4218         loadmodel->brushq3.num_leafbrushes = count;
4219
4220         for (i = 0;i < count;i++, in++, out++)
4221         {
4222                 n = LittleLong(*in);
4223                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4224                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4225                 *out = loadmodel->brushq3.data_brushes + n;
4226         }
4227 }
4228
4229 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4230 {
4231         int *in;
4232         q3mface_t **out;
4233         int i, n, count;
4234
4235         in = (void *)(mod_base + l->fileofs);
4236         if (l->filelen % sizeof(*in))
4237                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4238         count = l->filelen / sizeof(*in);
4239         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4240
4241         loadmodel->brushq3.data_leaffaces = out;
4242         loadmodel->brushq3.num_leaffaces = count;
4243
4244         for (i = 0;i < count;i++, in++, out++)
4245         {
4246                 n = LittleLong(*in);
4247                 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4248                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4249                 *out = loadmodel->brushq3.data_faces + n;
4250         }
4251 }
4252
4253 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4254 {
4255         q3dleaf_t *in;
4256         q3mleaf_t *out;
4257         int i, j, n, c, count;
4258
4259         in = (void *)(mod_base + l->fileofs);
4260         if (l->filelen % sizeof(*in))
4261                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4262         count = l->filelen / sizeof(*in);
4263         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4264
4265         loadmodel->brushq3.data_leafs = out;
4266         loadmodel->brushq3.num_leafs = count;
4267
4268         for (i = 0;i < count;i++, in++, out++)
4269         {
4270                 out->isnode = false;
4271                 out->parent = NULL;
4272                 out->clusterindex = LittleLong(in->clusterindex);
4273                 out->areaindex = LittleLong(in->areaindex);
4274                 for (j = 0;j < 3;j++)
4275                 {
4276                         // yes the mins/maxs are ints
4277                         out->mins[j] = LittleLong(in->mins[j]);
4278                         out->maxs[j] = LittleLong(in->maxs[j]);
4279                 }
4280                 n = LittleLong(in->firstleafface);
4281                 c = LittleLong(in->numleaffaces);
4282                 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4283                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4284                 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4285                 out->numleaffaces = c;
4286                 n = LittleLong(in->firstleafbrush);
4287                 c = LittleLong(in->numleafbrushes);
4288                 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4289                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4290                 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4291                 out->numleafbrushes = c;
4292         }
4293 }
4294
4295 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4296 {
4297         if (node->parent)
4298                 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4299         node->parent = parent;
4300         if (node->isnode)
4301         {
4302                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4303                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4304         }
4305 }
4306
4307 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4308 {
4309         q3dnode_t *in;
4310         q3mnode_t *out;
4311         int i, j, n, count;
4312
4313         in = (void *)(mod_base + l->fileofs);
4314         if (l->filelen % sizeof(*in))
4315                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4316         count = l->filelen / sizeof(*in);
4317         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4318
4319         loadmodel->brushq3.data_nodes = out;
4320         loadmodel->brushq3.num_nodes = count;
4321
4322         for (i = 0;i < count;i++, in++, out++)
4323         {
4324                 out->isnode = true;
4325                 out->parent = NULL;
4326                 n = LittleLong(in->planeindex);
4327                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4328                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4329                 out->plane = loadmodel->brushq3.data_planes + n;
4330                 for (j = 0;j < 2;j++)
4331                 {
4332                         n = LittleLong(in->childrenindex[j]);
4333                         if (n >= 0)
4334                         {
4335                                 if (n >= loadmodel->brushq3.num_nodes)
4336                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4337                                 out->children[j] = loadmodel->brushq3.data_nodes + n;
4338                         }
4339                         else
4340                         {
4341                                 n = 1 - n;
4342                                 if (n >= loadmodel->brushq3.num_leafs)
4343                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4344                                 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4345                         }
4346                 }
4347                 // we don't load the mins/maxs
4348         }
4349
4350         // set the parent pointers
4351         Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4352 }
4353
4354 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4355 {
4356         q3dlightgrid_t *in;
4357         q3dlightgrid_t *out;
4358         int count;
4359
4360         in = (void *)(mod_base + l->fileofs);
4361         if (l->filelen % sizeof(*in))
4362                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4363         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4364         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4365         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4366         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4367         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4368         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4369         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4370         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4371         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4372         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4373         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4374         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4375         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4376         if (l->filelen < count * (int)sizeof(*in))
4377                 Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)\n", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
4378         if (l->filelen != count * (int)sizeof(*in))
4379                 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4380
4381         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4382         loadmodel->brushq3.data_lightgrid = out;
4383         loadmodel->brushq3.num_lightgrid = count;
4384
4385         // no swapping or validation necessary
4386         memcpy(out, in, count * (int)sizeof(*out));
4387
4388         Matrix4x4_CreateScale3(&loadmodel->brushq3.num_lightgrid_indexfromworld, loadmodel->brushq3.num_lightgrid_scale[0], loadmodel->brushq3.num_lightgrid_scale[1], loadmodel->brushq3.num_lightgrid_scale[2]);
4389         Matrix4x4_ConcatTranslate(&loadmodel->brushq3.num_lightgrid_indexfromworld, -loadmodel->brushq3.num_lightgrid_imins[0] * loadmodel->brushq3.num_lightgrid_cellsize[0], -loadmodel->brushq3.num_lightgrid_imins[1] * loadmodel->brushq3.num_lightgrid_cellsize[1], -loadmodel->brushq3.num_lightgrid_imins[2] * loadmodel->brushq3.num_lightgrid_cellsize[2]);
4390 }
4391
4392 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4393 {
4394         q3dpvs_t *in;
4395         int totalchains;
4396
4397         in = (void *)(mod_base + l->fileofs);
4398         if (l->filelen < 9)
4399                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4400
4401         loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4402         loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4403         if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4404                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4405         totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4406         if (l->filelen < totalchains + (int)sizeof(*in))
4407                 Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)\n", loadmodel->brushq3.num_pvsclusters, loadmodel->brushq3.num_pvschainlength, totalchains + sizeof(*in), l->filelen);
4408
4409         loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4410         memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4411 }
4412
4413 void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4414 {
4415         // FIXME: finish this code
4416         VectorCopy(in, out);
4417 }
4418
4419 void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end)
4420 {
4421         if (node->isnode)
4422         {
4423                 // recurse down node sides
4424                 int i;
4425                 float dist;
4426                 colpointf_t *ps, *pe;
4427                 // FIXME? if TraceBrushPolygonTransform were to be made usable, the
4428                 // node planes would need to be transformed too
4429                 dist = node->plane->dist - (1.0f / 8.0f);
4430                 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4431                 {
4432                         if (DotProduct(ps->v, node->plane->normal) > dist || DotProduct(pe->v, node->plane->normal) > dist)
4433                         {
4434                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end);
4435                                 break;
4436                         }
4437                 }
4438                 dist = node->plane->dist + (1.0f / 8.0f);
4439                 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4440                 {
4441                         if (DotProduct(ps->v, node->plane->normal) < dist || DotProduct(pe->v, node->plane->normal) < dist)
4442                         {
4443                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end);
4444                                 break;
4445                         }
4446                 }
4447                 /*
4448                 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4449                 if (sides & 1)
4450                         Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4451                 if (sides & 2)
4452                         Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4453                 */
4454         }
4455         else
4456         {
4457                 int i;
4458                 q3mleaf_t *leaf;
4459                 leaf = (q3mleaf_t *)node;
4460                 for (i = 0;i < leaf->numleafbrushes;i++)
4461                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4462         }
4463 }
4464
4465 void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4466 {
4467         // FIXME: write this
4468         ambientcolor[0] += 255;
4469         ambientcolor[1] += 255;
4470         ambientcolor[2] += 255;
4471 }
4472
4473 void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs)
4474 {
4475         int i;
4476         colbrushf_t *thisbrush_start, *thisbrush_end;
4477         matrix4x4_t startmatrix, endmatrix;
4478         // FIXME: finish this code
4479         Matrix4x4_CreateIdentity(&startmatrix);
4480         Matrix4x4_CreateIdentity(&endmatrix);
4481         thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4482         thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4483         memset(trace, 0, sizeof(*trace));
4484         trace->fraction = 1;
4485         if (model->brushq3.num_nodes)
4486                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end);
4487         else
4488                 for (i = 0;i < model->brushq3.num_brushes;i++)
4489                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_brushes[i].colbrushf, model->brushq3.data_brushes[i].colbrushf);
4490 }
4491
4492
4493 static int Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const q3mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4494 {
4495         int clusterindex;
4496 loc0:
4497         if (!node->isnode)
4498         {
4499                 // leaf
4500                 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4501                 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4502         }
4503
4504         // node - recurse down the BSP tree
4505         switch (BoxOnPlaneSide(mins, maxs, node->plane))
4506         {
4507         case 1: // front
4508                 node = node->children[0];
4509                 goto loc0;
4510         case 2: // back
4511                 node = node->children[1];
4512                 goto loc0;
4513         default: // crossing
4514                 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4515                         return true;
4516                 node = node->children[1];
4517                 goto loc0;
4518         }
4519         // never reached
4520         return false;
4521 }
4522
4523 int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4524 {
4525         return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4526 }
4527
4528 int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4529 {
4530         // FIXME: write this
4531         memset(pvsbuffer, 0xFF, pvsbufferlength);
4532         return pvsbufferlength;
4533 }
4534
4535 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4536 {
4537         int i;
4538         q3dheader_t *header;
4539
4540         mod->type = mod_brushq2;
4541
4542         header = (q3dheader_t *)buffer;
4543
4544         i = LittleLong(header->version);
4545         if (i != Q3BSPVERSION)
4546                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4547         if (loadmodel->isworldmodel)
4548         {
4549                 Cvar_SetValue("halflifebsp", false);
4550                 // until we get a texture for it...
4551                 R_ResetQuakeSky();
4552         }
4553
4554         mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4555         mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4556         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4557         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4558         mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4559         //mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
4560         //mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
4561
4562         mod_base = (qbyte *)header;
4563
4564         // swap all the lumps
4565         for (i = 0;i < (int) sizeof(*header) / 4;i++)
4566                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4567
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]);
4585 }
4586
4587 void Mod_IBSP_Load(model_t *mod, void *buffer)
4588 {
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);
4594         else
4595                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4596 }
4597
4598 void Mod_MAP_Load(model_t *mod, void *buffer)
4599 {
4600         Host_Error("Mod_MAP_Load: not yet implemented\n");
4601 }
4602