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