995f0d0424535a0b5515a30156d98e260a91904b
[xonotic/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24 #include "winding.h"
25 #include "curves.h"
26
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
28
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
30
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
38 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
39 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
40 cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0"};
41
42 static void Mod_Q1BSP_Collision_Init (void);
43 void Mod_BrushInit(void)
44 {
45 //      Cvar_RegisterVariable(&r_subdivide_size);
46         Cvar_RegisterVariable(&halflifebsp);
47         Cvar_RegisterVariable(&r_novis);
48         Cvar_RegisterVariable(&r_miplightmaps);
49         Cvar_RegisterVariable(&r_lightmaprgba);
50         Cvar_RegisterVariable(&r_nosurftextures);
51         Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
52         Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
53         Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
54         Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
55         memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
56         Mod_Q1BSP_Collision_Init();
57 }
58
59 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
60 {
61         mnode_t *node;
62
63         if (model == NULL)
64                 return NULL;
65
66         Mod_CheckLoaded(model);
67
68         // LordHavoc: modified to start at first clip node,
69         // in other words: first node of the (sub)model
70         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
71         while (node->contents == 0)
72                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
73
74         return (mleaf_t *)node;
75 }
76
77 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
78 {
79         int i;
80         mleaf_t *leaf;
81         leaf = Mod_Q1BSP_PointInLeaf(model, p);
82         if (leaf)
83         {
84                 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
85                 if (i)
86                 {
87                         memcpy(out, leaf->ambient_sound_level, i);
88                         out += i;
89                         outsize -= i;
90                 }
91         }
92         if (outsize)
93                 memset(out, 0, outsize);
94 }
95
96
97 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)
98 {
99         int leafnum;
100 loc0:
101         if (node->contents < 0)
102         {
103                 // leaf
104                 if (node->contents == CONTENTS_SOLID)
105                         return false;
106                 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
107                 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
108         }
109
110         // node - recurse down the BSP tree
111         switch (BoxOnPlaneSide(mins, maxs, node->plane))
112         {
113         case 1: // front
114                 node = node->children[0];
115                 goto loc0;
116         case 2: // back
117                 node = node->children[1];
118                 goto loc0;
119         default: // crossing
120                 if (node->children[0]->contents != CONTENTS_SOLID)
121                         if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
122                                 return true;
123                 node = node->children[1];
124                 goto loc0;
125         }
126         // never reached
127         return false;
128 }
129
130 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
131 {
132         return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
133 }
134
135 /*
136 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
137 {
138         mnode_t *node;
139
140         if (model == NULL)
141                 return CONTENTS_EMPTY;
142
143         Mod_CheckLoaded(model);
144
145         // LordHavoc: modified to start at first clip node,
146         // in other words: first node of the (sub)model
147         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
148         while (node->contents == 0)
149                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
150
151         return ((mleaf_t *)node)->contents;
152 }
153 */
154
155 typedef struct findnonsolidlocationinfo_s
156 {
157         vec3_t center;
158         vec_t radius;
159         vec3_t nudge;
160         vec_t bestdist;
161         model_t *model;
162 }
163 findnonsolidlocationinfo_t;
164
165 #if 0
166 extern cvar_t samelevel;
167 #endif
168 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
169 {
170         int i, surfnum, k, *tri, *mark;
171         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
172 #if 0
173         float surfnormal[3];
174 #endif
175         msurface_t *surf;
176         for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
177         {
178                 surf = info->model->brushq1.surfaces + *mark;
179                 if (surf->flags & SURF_SOLIDCLIP)
180                 {
181 #if 0
182                         VectorCopy(surf->plane->normal, surfnormal);
183                         if (surf->flags & SURF_PLANEBACK)
184                                 VectorNegate(surfnormal, surfnormal);
185 #endif
186                         for (k = 0;k < surf->mesh.num_triangles;k++)
187                         {
188                                 tri = surf->mesh.data_element3i + k * 3;
189                                 VectorCopy((surf->mesh.data_vertex3f + tri[0] * 3), vert[0]);
190                                 VectorCopy((surf->mesh.data_vertex3f + tri[1] * 3), vert[1]);
191                                 VectorCopy((surf->mesh.data_vertex3f + tri[2] * 3), vert[2]);
192                                 VectorSubtract(vert[1], vert[0], edge[0]);
193                                 VectorSubtract(vert[2], vert[1], edge[1]);
194                                 CrossProduct(edge[1], edge[0], facenormal);
195                                 if (facenormal[0] || facenormal[1] || facenormal[2])
196                                 {
197                                         VectorNormalize(facenormal);
198 #if 0
199                                         if (VectorDistance(facenormal, surfnormal) > 0.01f)
200                                                 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
201 #endif
202                                         f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
203                                         if (f <= info->bestdist && f >= -info->bestdist)
204                                         {
205                                                 VectorSubtract(vert[0], vert[2], edge[2]);
206                                                 VectorNormalize(edge[0]);
207                                                 VectorNormalize(edge[1]);
208                                                 VectorNormalize(edge[2]);
209                                                 CrossProduct(facenormal, edge[0], edgenormal[0]);
210                                                 CrossProduct(facenormal, edge[1], edgenormal[1]);
211                                                 CrossProduct(facenormal, edge[2], edgenormal[2]);
212 #if 0
213                                                 if (samelevel.integer & 1)
214                                                         VectorNegate(edgenormal[0], edgenormal[0]);
215                                                 if (samelevel.integer & 2)
216                                                         VectorNegate(edgenormal[1], edgenormal[1]);
217                                                 if (samelevel.integer & 4)
218                                                         VectorNegate(edgenormal[2], edgenormal[2]);
219                                                 for (i = 0;i < 3;i++)
220                                                         if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
221                                                          || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
222                                                          || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
223                                                                 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]);
224 #endif
225                                                 // face distance
226                                                 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
227                                                  && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
228                                                  && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
229                                                 {
230                                                         // we got lucky, the center is within the face
231                                                         dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
232                                                         if (dist < 0)
233                                                         {
234                                                                 dist = -dist;
235                                                                 if (info->bestdist > dist)
236                                                                 {
237                                                                         info->bestdist = dist;
238                                                                         VectorScale(facenormal, (info->radius - -dist), info->nudge);
239                                                                 }
240                                                         }
241                                                         else
242                                                         {
243                                                                 if (info->bestdist > dist)
244                                                                 {
245                                                                         info->bestdist = dist;
246                                                                         VectorScale(facenormal, (info->radius - dist), info->nudge);
247                                                                 }
248                                                         }
249                                                 }
250                                                 else
251                                                 {
252                                                         // check which edge or vertex the center is nearest
253                                                         for (i = 0;i < 3;i++)
254                                                         {
255                                                                 f = DotProduct(info->center, edge[i]);
256                                                                 if (f >= DotProduct(vert[0], edge[i])
257                                                                  && f <= DotProduct(vert[1], edge[i]))
258                                                                 {
259                                                                         // on edge
260                                                                         VectorMA(info->center, -f, edge[i], point);
261                                                                         dist = sqrt(DotProduct(point, point));
262                                                                         if (info->bestdist > dist)
263                                                                         {
264                                                                                 info->bestdist = dist;
265                                                                                 VectorScale(point, (info->radius / dist), info->nudge);
266                                                                         }
267                                                                         // skip both vertex checks
268                                                                         // (both are further away than this edge)
269                                                                         i++;
270                                                                 }
271                                                                 else
272                                                                 {
273                                                                         // not on edge, check first vertex of edge
274                                                                         VectorSubtract(info->center, vert[i], point);
275                                                                         dist = sqrt(DotProduct(point, point));
276                                                                         if (info->bestdist > dist)
277                                                                         {
278                                                                                 info->bestdist = dist;
279                                                                                 VectorScale(point, (info->radius / dist), info->nudge);
280                                                                         }
281                                                                 }
282                                                         }
283                                                 }
284                                         }
285                                 }
286                         }
287                 }
288         }
289 }
290
291 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
292 {
293         if (node->contents)
294         {
295                 if (((mleaf_t *)node)->nummarksurfaces)
296                         Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
297         }
298         else
299         {
300                 float f = PlaneDiff(info->center, node->plane);
301                 if (f >= -info->bestdist)
302                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
303                 if (f <= info->bestdist)
304                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
305         }
306 }
307
308 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
309 {
310         int i;
311         findnonsolidlocationinfo_t info;
312         if (model == NULL)
313         {
314                 VectorCopy(in, out);
315                 return;
316         }
317         VectorCopy(in, info.center);
318         info.radius = radius;
319         info.model = model;
320         i = 0;
321         do
322         {
323                 VectorClear(info.nudge);
324                 info.bestdist = radius;
325                 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
326                 VectorAdd(info.center, info.nudge, info.center);
327         }
328         while (info.bestdist < radius && ++i < 10);
329         VectorCopy(info.center, out);
330 }
331
332 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
333 {
334         switch(nativecontents)
335         {
336                 case CONTENTS_EMPTY:
337                         return 0;
338                 case CONTENTS_SOLID:
339                         return SUPERCONTENTS_SOLID;
340                 case CONTENTS_WATER:
341                         return SUPERCONTENTS_WATER;
342                 case CONTENTS_SLIME:
343                         return SUPERCONTENTS_SLIME;
344                 case CONTENTS_LAVA:
345                         return SUPERCONTENTS_LAVA;
346                 case CONTENTS_SKY:
347                         return SUPERCONTENTS_SKY;
348         }
349         return 0;
350 }
351
352 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
353 {
354         if (supercontents & SUPERCONTENTS_SOLID)
355                 return CONTENTS_SOLID;
356         if (supercontents & SUPERCONTENTS_SKY)
357                 return CONTENTS_SKY;
358         if (supercontents & SUPERCONTENTS_LAVA)
359                 return CONTENTS_LAVA;
360         if (supercontents & SUPERCONTENTS_SLIME)
361                 return CONTENTS_SLIME;
362         if (supercontents & SUPERCONTENTS_WATER)
363                 return CONTENTS_WATER;
364         return CONTENTS_EMPTY;
365 }
366
367 typedef struct
368 {
369         // the hull we're tracing through
370         const hull_t *hull;
371
372         // the trace structure to fill in
373         trace_t *trace;
374
375         // start, end, and end - start (in model space)
376         double start[3];
377         double end[3];
378         double dist[3];
379 }
380 RecursiveHullCheckTraceInfo_t;
381
382 // 1/32 epsilon to keep floating point happy
383 #define DIST_EPSILON (0.03125)
384
385 #define HULLCHECKSTATE_EMPTY 0
386 #define HULLCHECKSTATE_SOLID 1
387 #define HULLCHECKSTATE_DONE 2
388
389 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
390 {
391         // status variables, these don't need to be saved on the stack when
392         // recursing...  but are because this should be thread-safe
393         // (note: tracing against a bbox is not thread-safe, yet)
394         int ret;
395         mplane_t *plane;
396         double t1, t2;
397
398         // variables that need to be stored on the stack when recursing
399         dclipnode_t *node;
400         int side;
401         double midf, mid[3];
402
403         // LordHavoc: a goto!  everyone flee in terror... :)
404 loc0:
405         // check for empty
406         if (num < 0)
407         {
408                 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
409                 if (!t->trace->startfound)
410                 {
411                         t->trace->startfound = true;
412                         t->trace->startsupercontents |= num;
413                 }
414                 if (num & SUPERCONTENTS_LIQUIDSMASK)
415                         t->trace->inwater = true;
416                 if (num == 0)
417                         t->trace->inopen = true;
418                 if (num & t->trace->hitsupercontentsmask)
419                 {
420                         // if the first leaf is solid, set startsolid
421                         if (t->trace->allsolid)
422                                 t->trace->startsolid = true;
423 #if COLLISIONPARANOID >= 3
424                         Con_Printf("S");
425 #endif
426                         return HULLCHECKSTATE_SOLID;
427                 }
428                 else
429                 {
430                         t->trace->allsolid = false;
431 #if COLLISIONPARANOID >= 3
432                         Con_Printf("E");
433 #endif
434                         return HULLCHECKSTATE_EMPTY;
435                 }
436         }
437
438         // find the point distances
439         node = t->hull->clipnodes + num;
440
441         plane = t->hull->planes + node->planenum;
442         if (plane->type < 3)
443         {
444                 t1 = p1[plane->type] - plane->dist;
445                 t2 = p2[plane->type] - plane->dist;
446         }
447         else
448         {
449                 t1 = DotProduct (plane->normal, p1) - plane->dist;
450                 t2 = DotProduct (plane->normal, p2) - plane->dist;
451         }
452
453         if (t1 < 0)
454         {
455                 if (t2 < 0)
456                 {
457 #if COLLISIONPARANOID >= 3
458                         Con_Printf("<");
459 #endif
460                         num = node->children[1];
461                         goto loc0;
462                 }
463                 side = 1;
464         }
465         else
466         {
467                 if (t2 >= 0)
468                 {
469 #if COLLISIONPARANOID >= 3
470                         Con_Printf(">");
471 #endif
472                         num = node->children[0];
473                         goto loc0;
474                 }
475                 side = 0;
476         }
477
478         // the line intersects, find intersection point
479         // LordHavoc: this uses the original trace for maximum accuracy
480 #if COLLISIONPARANOID >= 3
481         Con_Printf("M");
482 #endif
483         if (plane->type < 3)
484         {
485                 t1 = t->start[plane->type] - plane->dist;
486                 t2 = t->end[plane->type] - plane->dist;
487         }
488         else
489         {
490                 t1 = DotProduct (plane->normal, t->start) - plane->dist;
491                 t2 = DotProduct (plane->normal, t->end) - plane->dist;
492         }
493
494         midf = t1 / (t1 - t2);
495         midf = bound(p1f, midf, p2f);
496         VectorMA(t->start, midf, t->dist, mid);
497
498         // recurse both sides, front side first
499         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
500         // if this side is not empty, return what it is (solid or done)
501         if (ret != HULLCHECKSTATE_EMPTY)
502                 return ret;
503
504         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
505         // if other side is not solid, return what it is (empty or done)
506         if (ret != HULLCHECKSTATE_SOLID)
507                 return ret;
508
509         // front is air and back is solid, this is the impact point...
510         if (side)
511         {
512                 t->trace->plane.dist = -plane->dist;
513                 VectorNegate (plane->normal, t->trace->plane.normal);
514         }
515         else
516         {
517                 t->trace->plane.dist = plane->dist;
518                 VectorCopy (plane->normal, t->trace->plane.normal);
519         }
520
521         // calculate the true fraction
522         t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist;
523         t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist;
524         midf = t1 / (t1 - t2);
525         t->trace->realfraction = bound(0, midf, 1);
526
527         // calculate the return fraction which is nudged off the surface a bit
528         midf = (t1 - DIST_EPSILON) / (t1 - t2);
529         t->trace->fraction = bound(0, midf, 1);
530
531 #if COLLISIONPARANOID >= 3
532         Con_Printf("D");
533 #endif
534         return HULLCHECKSTATE_DONE;
535 }
536
537 #if COLLISIONPARANOID < 2
538 static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
539 {
540         while (num >= 0)
541                 num = t->hull->clipnodes[num].children[(t->hull->planes[t->hull->clipnodes[num].planenum].type < 3 ? t->start[t->hull->planes[t->hull->clipnodes[num].planenum].type] : DotProduct(t->hull->planes[t->hull->clipnodes[num].planenum].normal, t->start)) < t->hull->planes[t->hull->clipnodes[num].planenum].dist];
542         num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
543         t->trace->startsupercontents |= num;
544         if (num & SUPERCONTENTS_LIQUIDSMASK)
545                 t->trace->inwater = true;
546         if (num == 0)
547                 t->trace->inopen = true;
548         if (num & t->trace->hitsupercontentsmask)
549         {
550                 t->trace->allsolid = t->trace->startsolid = true;
551                 return HULLCHECKSTATE_SOLID;
552         }
553         else
554         {
555                 t->trace->allsolid = t->trace->startsolid = false;
556                 return HULLCHECKSTATE_EMPTY;
557         }
558 }
559 #endif
560
561 static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
562 {
563         // this function currently only supports same size start and end
564         double boxsize[3];
565         RecursiveHullCheckTraceInfo_t rhc;
566
567         memset(&rhc, 0, sizeof(rhc));
568         memset(trace, 0, sizeof(trace_t));
569         rhc.trace = trace;
570         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
571         rhc.trace->fraction = 1;
572         rhc.trace->realfraction = 1;
573         rhc.trace->allsolid = true;
574         VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
575         if (boxsize[0] < 3)
576                 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
577         else if (model->brush.ishlbsp)
578         {
579                 // LordHavoc: this has to have a minor tolerance (the .1) because of
580                 // minor float precision errors from the box being transformed around
581                 if (boxsize[0] < 32.1)
582                 {
583                         if (boxsize[2] < 54) // pick the nearest of 36 or 72
584                                 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
585                         else
586                                 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
587                 }
588                 else
589                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
590         }
591         else
592         {
593                 // LordHavoc: this has to have a minor tolerance (the .1) because of
594                 // minor float precision errors from the box being transformed around
595                 if (boxsize[0] < 32.1)
596                         rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
597                 else
598                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
599         }
600         VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
601         VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
602         VectorSubtract(rhc.end, rhc.start, rhc.dist);
603 #if COLLISIONPARANOID >= 2
604         Con_Printf("t(%f %f %f,%f %f %f,%i %f %f %f)", rhc.start[0], rhc.start[1], rhc.start[2], rhc.end[0], rhc.end[1], rhc.end[2], rhc.hull - model->brushq1.hulls, rhc.hull->clip_mins[0], rhc.hull->clip_mins[1], rhc.hull->clip_mins[2]);
605         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
606         Con_Printf("\n");
607 #else
608         if (DotProduct(rhc.dist, rhc.dist))
609                 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
610         else
611                 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
612 #endif
613 }
614
615 static hull_t box_hull;
616 static dclipnode_t box_clipnodes[6];
617 static mplane_t box_planes[6];
618
619 static void Mod_Q1BSP_Collision_Init (void)
620 {
621         int             i;
622         int             side;
623
624         //Set up the planes and clipnodes so that the six floats of a bounding box
625         //can just be stored out and get a proper hull_t structure.
626
627         box_hull.clipnodes = box_clipnodes;
628         box_hull.planes = box_planes;
629         box_hull.firstclipnode = 0;
630         box_hull.lastclipnode = 5;
631
632         for (i = 0;i < 6;i++)
633         {
634                 box_clipnodes[i].planenum = i;
635
636                 side = i&1;
637
638                 box_clipnodes[i].children[side] = CONTENTS_EMPTY;
639                 if (i != 5)
640                         box_clipnodes[i].children[side^1] = i + 1;
641                 else
642                         box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
643
644                 box_planes[i].type = i>>1;
645                 box_planes[i].normal[i>>1] = 1;
646         }
647 }
648
649 void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cmaxs, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int hitsupercontentsmask, int boxsupercontents)
650 {
651 #if 1
652         colbrushf_t cbox;
653         colplanef_t cbox_planes[6];
654         cbox.supercontents = boxsupercontents;
655         cbox.numplanes = 6;
656         cbox.numpoints = 0;
657         cbox.numtriangles = 0;
658         cbox.planes = cbox_planes;
659         cbox.points = NULL;
660         cbox.elements = NULL;
661         cbox.markframe = 0;
662         cbox.mins[0] = 0;
663         cbox.mins[1] = 0;
664         cbox.mins[2] = 0;
665         cbox.maxs[0] = 0;
666         cbox.maxs[1] = 0;
667         cbox.maxs[2] = 0;
668         cbox_planes[0].normal[0] =  1;cbox_planes[0].normal[1] =  0;cbox_planes[0].normal[2] =  0;cbox_planes[0].dist = cmaxs[0] - mins[0];
669         cbox_planes[1].normal[0] = -1;cbox_planes[1].normal[1] =  0;cbox_planes[1].normal[2] =  0;cbox_planes[1].dist = maxs[0] - cmins[0];
670         cbox_planes[2].normal[0] =  0;cbox_planes[2].normal[1] =  1;cbox_planes[2].normal[2] =  0;cbox_planes[2].dist = cmaxs[1] - mins[1];
671         cbox_planes[3].normal[0] =  0;cbox_planes[3].normal[1] = -1;cbox_planes[3].normal[2] =  0;cbox_planes[3].dist = maxs[1] - cmins[1];
672         cbox_planes[4].normal[0] =  0;cbox_planes[4].normal[1] =  0;cbox_planes[4].normal[2] =  1;cbox_planes[4].dist = cmaxs[2] - mins[2];
673         cbox_planes[5].normal[0] =  0;cbox_planes[5].normal[1] =  0;cbox_planes[5].normal[2] = -1;cbox_planes[5].dist = maxs[2] - cmins[2];
674         memset(trace, 0, sizeof(trace_t));
675         trace->hitsupercontentsmask = hitsupercontentsmask;
676         trace->fraction = 1;
677         trace->realfraction = 1;
678         Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox);
679 #else
680         RecursiveHullCheckTraceInfo_t rhc;
681         // fill in a default trace
682         memset(&rhc, 0, sizeof(rhc));
683         memset(trace, 0, sizeof(trace_t));
684         //To keep everything totally uniform, bounding boxes are turned into small
685         //BSP trees instead of being compared directly.
686         // create a temp hull from bounding box sizes
687         box_planes[0].dist = cmaxs[0] - mins[0];
688         box_planes[1].dist = cmins[0] - maxs[0];
689         box_planes[2].dist = cmaxs[1] - mins[1];
690         box_planes[3].dist = cmins[1] - maxs[1];
691         box_planes[4].dist = cmaxs[2] - mins[2];
692         box_planes[5].dist = cmins[2] - maxs[2];
693 #if COLLISIONPARANOID >= 3
694         Con_Printf("box_planes %f:%f %f:%f %f:%f\ncbox %f %f %f:%f %f %f\nbox %f %f %f:%f %f %f\n", box_planes[0].dist, box_planes[1].dist, box_planes[2].dist, box_planes[3].dist, box_planes[4].dist, box_planes[5].dist, cmins[0], cmins[1], cmins[2], cmaxs[0], cmaxs[1], cmaxs[2], mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
695 #endif
696         // trace a line through the generated clipping hull
697         //rhc.boxsupercontents = boxsupercontents;
698         rhc.hull = &box_hull;
699         rhc.trace = trace;
700         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
701         rhc.trace->fraction = 1;
702         rhc.trace->realfraction = 1;
703         rhc.trace->allsolid = true;
704         VectorCopy(start, rhc.start);
705         VectorCopy(end, rhc.end);
706         VectorSubtract(rhc.end, rhc.start, rhc.dist);
707         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
708         //VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
709         if (rhc.trace->startsupercontents)
710                 rhc.trace->startsupercontents = boxsupercontents;
711 #endif
712 }
713
714 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)
715 {
716         int side, distz = endz - startz;
717         float front, back;
718         float mid;
719
720 loc0:
721         if (node->contents < 0)
722                 return false;           // didn't hit anything
723
724         switch (node->plane->type)
725         {
726         case PLANE_X:
727                 node = node->children[x < node->plane->dist];
728                 goto loc0;
729         case PLANE_Y:
730                 node = node->children[y < node->plane->dist];
731                 goto loc0;
732         case PLANE_Z:
733                 side = startz < node->plane->dist;
734                 if ((endz < node->plane->dist) == side)
735                 {
736                         node = node->children[side];
737                         goto loc0;
738                 }
739                 // found an intersection
740                 mid = node->plane->dist;
741                 break;
742         default:
743                 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
744                 front += startz * node->plane->normal[2];
745                 back += endz * node->plane->normal[2];
746                 side = front < node->plane->dist;
747                 if ((back < node->plane->dist) == side)
748                 {
749                         node = node->children[side];
750                         goto loc0;
751                 }
752                 // found an intersection
753                 mid = startz + distz * (front - node->plane->dist) / (front - back);
754                 break;
755         }
756
757         // go down front side
758         if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
759                 return true;    // hit something
760         else
761         {
762                 // check for impact on this node
763                 if (node->numsurfaces)
764                 {
765                         int i, ds, dt;
766                         msurface_t *surf;
767
768                         surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
769                         for (i = 0;i < node->numsurfaces;i++, surf++)
770                         {
771                                 if (!(surf->flags & SURF_LIGHTMAP))
772                                         continue;       // no lightmaps
773
774                                 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]);
775                                 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]);
776
777                                 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
778                                         continue;
779
780                                 ds -= surf->texturemins[0];
781                                 dt -= surf->texturemins[1];
782
783                                 if (ds > surf->extents[0] || dt > surf->extents[1])
784                                         continue;
785
786                                 if (surf->samples)
787                                 {
788                                         qbyte *lightmap;
789                                         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;
790                                         line3 = ((surf->extents[0]>>4)+1)*3;
791                                         size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
792
793                                         lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
794
795                                         for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
796                                         {
797                                                 scale = d_lightstylevalue[surf->styles[maps]];
798                                                 r00 += lightmap[      0] * scale;g00 += lightmap[      1] * scale;b00 += lightmap[      2] * scale;
799                                                 r01 += lightmap[      3] * scale;g01 += lightmap[      4] * scale;b01 += lightmap[      5] * scale;
800                                                 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
801                                                 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
802                                                 lightmap += size3;
803                                         }
804
805 /*
806 LordHavoc: here's the readable version of the interpolation
807 code, not quite as easy for the compiler to optimize...
808
809 dsfrac is the X position in the lightmap pixel, * 16
810 dtfrac is the Y position in the lightmap pixel, * 16
811 r00 is top left corner, r01 is top right corner
812 r10 is bottom left corner, r11 is bottom right corner
813 g and b are the same layout.
814 r0 and r1 are the top and bottom intermediate results
815
816 first we interpolate the top two points, to get the top
817 edge sample
818
819         r0 = (((r01-r00) * dsfrac) >> 4) + r00;
820         g0 = (((g01-g00) * dsfrac) >> 4) + g00;
821         b0 = (((b01-b00) * dsfrac) >> 4) + b00;
822
823 then we interpolate the bottom two points, to get the
824 bottom edge sample
825
826         r1 = (((r11-r10) * dsfrac) >> 4) + r10;
827         g1 = (((g11-g10) * dsfrac) >> 4) + g10;
828         b1 = (((b11-b10) * dsfrac) >> 4) + b10;
829
830 then we interpolate the top and bottom samples to get the
831 middle sample (the one which was requested)
832
833         r = (((r1-r0) * dtfrac) >> 4) + r0;
834         g = (((g1-g0) * dtfrac) >> 4) + g0;
835         b = (((b1-b0) * dtfrac) >> 4) + b0;
836 */
837
838                                         ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
839                                         ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
840                                         ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
841                                 }
842                                 return true; // success
843                         }
844                 }
845
846                 // go down back side
847                 node = node->children[side ^ 1];
848                 startz = mid;
849                 distz = endz - startz;
850                 goto loc0;
851         }
852 }
853
854 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
855 {
856         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);
857 }
858
859 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
860 {
861         int c;
862         qbyte *outstart = out;
863         while (out < outend)
864         {
865                 if (in == inend)
866                 {
867                         Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
868                         return;
869                 }
870                 c = *in++;
871                 if (c)
872                         *out++ = c;
873                 else
874                 {
875                         if (in == inend)
876                         {
877                                 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
878                                 return;
879                         }
880                         for (c = *in++;c > 0;c--)
881                         {
882                                 if (out == outend)
883                                 {
884                                         Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
885                                         return;
886                                 }
887                                 *out++ = 0;
888                         }
889                 }
890         }
891 }
892
893 static void Mod_Q1BSP_LoadTextures(lump_t *l)
894 {
895         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
896         miptex_t *dmiptex;
897         texture_t *tx, *tx2, *anims[10], *altanims[10];
898         dmiptexlump_t *m;
899         qbyte *data, *mtdata;
900         char name[256];
901
902         loadmodel->brushq1.textures = NULL;
903
904         // add two slots for notexture walls and notexture liquids
905         if (l->filelen)
906         {
907                 m = (dmiptexlump_t *)(mod_base + l->fileofs);
908                 m->nummiptex = LittleLong (m->nummiptex);
909                 loadmodel->brushq1.numtextures = m->nummiptex + 2;
910         }
911         else
912         {
913                 m = NULL;
914                 loadmodel->brushq1.numtextures = 2;
915         }
916
917         loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
918
919         // fill out all slots with notexture
920         for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
921         {
922                 tx->number = i;
923                 strcpy(tx->name, "NO TEXTURE FOUND");
924                 tx->width = 16;
925                 tx->height = 16;
926                 tx->skin.base = r_notexture;
927                 tx->shader = &Cshader_wall_lightmap;
928                 tx->flags = SURF_SOLIDCLIP;
929                 if (i == loadmodel->brushq1.numtextures - 1)
930                 {
931                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
932                         tx->shader = &Cshader_water;
933                 }
934                 tx->currentframe = tx;
935         }
936
937         if (!m)
938                 return;
939
940         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
941         dofs = m->dataofs;
942         // LordHavoc: mostly rewritten map texture loader
943         for (i = 0;i < m->nummiptex;i++)
944         {
945                 dofs[i] = LittleLong(dofs[i]);
946                 if (dofs[i] == -1 || r_nosurftextures.integer)
947                         continue;
948                 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
949
950                 // make sure name is no more than 15 characters
951                 for (j = 0;dmiptex->name[j] && j < 15;j++)
952                         name[j] = dmiptex->name[j];
953                 name[j] = 0;
954
955                 mtwidth = LittleLong(dmiptex->width);
956                 mtheight = LittleLong(dmiptex->height);
957                 mtdata = NULL;
958                 j = LittleLong(dmiptex->offsets[0]);
959                 if (j)
960                 {
961                         // texture included
962                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
963                         {
964                                 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
965                                 continue;
966                         }
967                         mtdata = (qbyte *)dmiptex + j;
968                 }
969
970                 if ((mtwidth & 15) || (mtheight & 15))
971                         Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
972
973                 // LordHavoc: force all names to lowercase
974                 for (j = 0;name[j];j++)
975                         if (name[j] >= 'A' && name[j] <= 'Z')
976                                 name[j] += 'a' - 'A';
977
978                 tx = loadmodel->brushq1.textures + i;
979                 strcpy(tx->name, name);
980                 tx->width = mtwidth;
981                 tx->height = mtheight;
982
983                 if (!tx->name[0])
984                 {
985                         sprintf(tx->name, "unnamed%i", i);
986                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
987                 }
988
989                 // LordHavoc: HL sky textures are entirely different than quake
990                 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
991                 {
992                         if (loadmodel->isworldmodel)
993                         {
994                                 data = loadimagepixels(tx->name, false, 0, 0);
995                                 if (data)
996                                 {
997                                         if (image_width == 256 && image_height == 128)
998                                         {
999                                                 R_InitSky(data, 4);
1000                                                 Mem_Free(data);
1001                                         }
1002                                         else
1003                                         {
1004                                                 Mem_Free(data);
1005                                                 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
1006                                                 if (mtdata != NULL)
1007                                                         R_InitSky(mtdata, 1);
1008                                         }
1009                                 }
1010                                 else if (mtdata != NULL)
1011                                         R_InitSky(mtdata, 1);
1012                         }
1013                 }
1014                 else
1015                 {
1016                         if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
1017                         {
1018                                 // did not find external texture, load it from the bsp or wad3
1019                                 if (loadmodel->brush.ishlbsp)
1020                                 {
1021                                         // internal texture overrides wad
1022                                         qbyte *pixels, *freepixels, *fogpixels;
1023                                         pixels = freepixels = NULL;
1024                                         if (mtdata)
1025                                                 pixels = W_ConvertWAD3Texture(dmiptex);
1026                                         if (pixels == NULL)
1027                                                 pixels = freepixels = W_GetTexture(tx->name);
1028                                         if (pixels != NULL)
1029                                         {
1030                                                 tx->width = image_width;
1031                                                 tx->height = image_height;
1032                                                 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);
1033                                                 if (Image_CheckAlpha(pixels, image_width * image_height, true))
1034                                                 {
1035                                                         fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
1036                                                         for (j = 0;j < image_width * image_height * 4;j += 4)
1037                                                         {
1038                                                                 fogpixels[j + 0] = 255;
1039                                                                 fogpixels[j + 1] = 255;
1040                                                                 fogpixels[j + 2] = 255;
1041                                                                 fogpixels[j + 3] = pixels[j + 3];
1042                                                         }
1043                                                         tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
1044                                                         Mem_Free(fogpixels);
1045                                                 }
1046                                         }
1047                                         if (freepixels)
1048                                                 Mem_Free(freepixels);
1049                                 }
1050                                 else if (mtdata) // texture included
1051                                         Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
1052                         }
1053                 }
1054                 if (tx->skin.base == NULL)
1055                 {
1056                         // no texture found
1057                         tx->width = 16;
1058                         tx->height = 16;
1059                         tx->skin.base = r_notexture;
1060                 }
1061
1062                 if (tx->name[0] == '*')
1063                 {
1064                         // turb does not block movement
1065                         tx->flags &= ~SURF_SOLIDCLIP;
1066                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
1067                         // LordHavoc: some turbulent textures should be fullbright and solid
1068                         if (!strncmp(tx->name,"*lava",5)
1069                          || !strncmp(tx->name,"*teleport",9)
1070                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
1071                                 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
1072                         else
1073                                 tx->flags |= SURF_WATERALPHA;
1074                         tx->shader = &Cshader_water;
1075                 }
1076                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
1077                 {
1078                         tx->flags |= SURF_DRAWSKY;
1079                         tx->shader = &Cshader_sky;
1080                 }
1081                 else
1082                 {
1083                         tx->flags |= SURF_LIGHTMAP;
1084                         if (!tx->skin.fog)
1085                                 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
1086                         tx->shader = &Cshader_wall_lightmap;
1087                 }
1088
1089                 // start out with no animation
1090                 tx->currentframe = tx;
1091         }
1092
1093         // sequence the animations
1094         for (i = 0;i < m->nummiptex;i++)
1095         {
1096                 tx = loadmodel->brushq1.textures + i;
1097                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
1098                         continue;
1099                 if (tx->anim_total[0] || tx->anim_total[1])
1100                         continue;       // already sequenced
1101
1102                 // find the number of frames in the animation
1103                 memset(anims, 0, sizeof(anims));
1104                 memset(altanims, 0, sizeof(altanims));
1105
1106                 for (j = i;j < m->nummiptex;j++)
1107                 {
1108                         tx2 = loadmodel->brushq1.textures + j;
1109                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
1110                                 continue;
1111
1112                         num = tx2->name[1];
1113                         if (num >= '0' && num <= '9')
1114                                 anims[num - '0'] = tx2;
1115                         else if (num >= 'a' && num <= 'j')
1116                                 altanims[num - 'a'] = tx2;
1117                         else
1118                                 Con_Printf("Bad animating texture %s\n", tx->name);
1119                 }
1120
1121                 max = altmax = 0;
1122                 for (j = 0;j < 10;j++)
1123                 {
1124                         if (anims[j])
1125                                 max = j + 1;
1126                         if (altanims[j])
1127                                 altmax = j + 1;
1128                 }
1129                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
1130
1131                 incomplete = false;
1132                 for (j = 0;j < max;j++)
1133                 {
1134                         if (!anims[j])
1135                         {
1136                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
1137                                 incomplete = true;
1138                         }
1139                 }
1140                 for (j = 0;j < altmax;j++)
1141                 {
1142                         if (!altanims[j])
1143                         {
1144                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
1145                                 incomplete = true;
1146                         }
1147                 }
1148                 if (incomplete)
1149                         continue;
1150
1151                 if (altmax < 1)
1152                 {
1153                         // if there is no alternate animation, duplicate the primary
1154                         // animation into the alternate
1155                         altmax = max;
1156                         for (k = 0;k < 10;k++)
1157                                 altanims[k] = anims[k];
1158                 }
1159
1160                 // link together the primary animation
1161                 for (j = 0;j < max;j++)
1162                 {
1163                         tx2 = anims[j];
1164                         tx2->animated = true;
1165                         tx2->anim_total[0] = max;
1166                         tx2->anim_total[1] = altmax;
1167                         for (k = 0;k < 10;k++)
1168                         {
1169                                 tx2->anim_frames[0][k] = anims[k];
1170                                 tx2->anim_frames[1][k] = altanims[k];
1171                         }
1172                 }
1173
1174                 // if there really is an alternate anim...
1175                 if (anims[0] != altanims[0])
1176                 {
1177                         // link together the alternate animation
1178                         for (j = 0;j < altmax;j++)
1179                         {
1180                                 tx2 = altanims[j];
1181                                 tx2->animated = true;
1182                                 // the primary/alternate are reversed here
1183                                 tx2->anim_total[0] = altmax;
1184                                 tx2->anim_total[1] = max;
1185                                 for (k = 0;k < 10;k++)
1186                                 {
1187                                         tx2->anim_frames[0][k] = altanims[k];
1188                                         tx2->anim_frames[1][k] = anims[k];
1189                                 }
1190                         }
1191                 }
1192         }
1193 }
1194
1195 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1196 {
1197         int i;
1198         qbyte *in, *out, *data, d;
1199         char litfilename[1024];
1200         loadmodel->brushq1.lightdata = NULL;
1201         if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1202         {
1203                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1204                 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1205         }
1206         else // LordHavoc: bsp version 29 (normal white lighting)
1207         {
1208                 // LordHavoc: hope is not lost yet, check for a .lit file to load
1209                 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1210                 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1211                 strlcat (litfilename, ".lit", sizeof (litfilename));
1212                 data = (qbyte*) FS_LoadFile(litfilename, false);
1213                 if (data)
1214                 {
1215                         if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1216                         {
1217                                 i = LittleLong(((int *)data)[1]);
1218                                 if (i == 1)
1219                                 {
1220                                         Con_DPrintf("loaded %s\n", litfilename);
1221                                         loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1222                                         memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1223                                         Mem_Free(data);
1224                                         return;
1225                                 }
1226                                 else
1227                                 {
1228                                         Con_Printf("Unknown .lit file version (%d)\n", i);
1229                                         Mem_Free(data);
1230                                 }
1231                         }
1232                         else
1233                         {
1234                                 if (fs_filesize == 8)
1235                                         Con_Printf("Empty .lit file, ignoring\n");
1236                                 else
1237                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1238                                 Mem_Free(data);
1239                         }
1240                 }
1241                 // LordHavoc: oh well, expand the white lighting data
1242                 if (!l->filelen)
1243                         return;
1244                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1245                 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1246                 out = loadmodel->brushq1.lightdata;
1247                 memcpy(in, mod_base + l->fileofs, l->filelen);
1248                 for (i = 0;i < l->filelen;i++)
1249                 {
1250                         d = *in++;
1251                         *out++ = d;
1252                         *out++ = d;
1253                         *out++ = d;
1254                 }
1255         }
1256 }
1257
1258 static void Mod_Q1BSP_LoadLightList(void)
1259 {
1260         int a, n, numlights;
1261         char lightsfilename[1024], *s, *t, *lightsstring;
1262         mlight_t *e;
1263
1264         strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1265         FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1266         strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1267         s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1268         if (s)
1269         {
1270                 numlights = 0;
1271                 while (*s)
1272                 {
1273                         while (*s && *s != '\n')
1274                                 s++;
1275                         if (!*s)
1276                         {
1277                                 Mem_Free(lightsstring);
1278                                 Host_Error("lights file must end with a newline\n");
1279                         }
1280                         s++;
1281                         numlights++;
1282                 }
1283                 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1284                 s = lightsstring;
1285                 n = 0;
1286                 while (*s && n < numlights)
1287                 {
1288                         t = s;
1289                         while (*s && *s != '\n')
1290                                 s++;
1291                         if (!*s)
1292                         {
1293                                 Mem_Free(lightsstring);
1294                                 Host_Error("misparsed lights file!\n");
1295                         }
1296                         e = loadmodel->brushq1.lights + n;
1297                         *s = 0;
1298                         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);
1299                         *s = '\n';
1300                         if (a != 14)
1301                         {
1302                                 Mem_Free(lightsstring);
1303                                 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);
1304                         }
1305                         s++;
1306                         n++;
1307                 }
1308                 if (*s)
1309                 {
1310                         Mem_Free(lightsstring);
1311                         Host_Error("misparsed lights file!\n");
1312                 }
1313                 loadmodel->brushq1.numlights = numlights;
1314                 Mem_Free(lightsstring);
1315         }
1316 }
1317
1318 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1319 {
1320         loadmodel->brushq1.num_compressedpvs = 0;
1321         loadmodel->brushq1.data_compressedpvs = NULL;
1322         if (!l->filelen)
1323                 return;
1324         loadmodel->brushq1.num_compressedpvs = l->filelen;
1325         loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1326         memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1327 }
1328
1329 // used only for HalfLife maps
1330 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1331 {
1332         char key[128], value[4096];
1333         char wadname[128];
1334         int i, j, k;
1335         if (!data)
1336                 return;
1337         if (!COM_ParseToken(&data, false))
1338                 return; // error
1339         if (com_token[0] != '{')
1340                 return; // error
1341         while (1)
1342         {
1343                 if (!COM_ParseToken(&data, false))
1344                         return; // error
1345                 if (com_token[0] == '}')
1346                         break; // end of worldspawn
1347                 if (com_token[0] == '_')
1348                         strcpy(key, com_token + 1);
1349                 else
1350                         strcpy(key, com_token);
1351                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1352                         key[strlen(key)-1] = 0;
1353                 if (!COM_ParseToken(&data, false))
1354                         return; // error
1355                 strcpy(value, com_token);
1356                 if (!strcmp("wad", key)) // for HalfLife maps
1357                 {
1358                         if (loadmodel->brush.ishlbsp)
1359                         {
1360                                 j = 0;
1361                                 for (i = 0;i < 4096;i++)
1362                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1363                                                 break;
1364                                 if (value[i])
1365                                 {
1366                                         for (;i < 4096;i++)
1367                                         {
1368                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1369                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1370                                                         j = i+1;
1371                                                 else if (value[i] == ';' || value[i] == 0)
1372                                                 {
1373                                                         k = value[i];
1374                                                         value[i] = 0;
1375                                                         strcpy(wadname, "textures/");
1376                                                         strcat(wadname, &value[j]);
1377                                                         W_LoadTextureWadFile(wadname, false);
1378                                                         j = i+1;
1379                                                         if (!k)
1380                                                                 break;
1381                                                 }
1382                                         }
1383                                 }
1384                         }
1385                 }
1386         }
1387 }
1388
1389 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1390 {
1391         loadmodel->brush.entities = NULL;
1392         if (!l->filelen)
1393                 return;
1394         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1395         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1396         if (loadmodel->brush.ishlbsp)
1397                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1398 }
1399
1400
1401 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1402 {
1403         dvertex_t       *in;
1404         mvertex_t       *out;
1405         int                     i, count;
1406
1407         in = (void *)(mod_base + l->fileofs);
1408         if (l->filelen % sizeof(*in))
1409                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1410         count = l->filelen / sizeof(*in);
1411         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1412
1413         loadmodel->brushq1.vertexes = out;
1414         loadmodel->brushq1.numvertexes = count;
1415
1416         for ( i=0 ; i<count ; i++, in++, out++)
1417         {
1418                 out->position[0] = LittleFloat(in->point[0]);
1419                 out->position[1] = LittleFloat(in->point[1]);
1420                 out->position[2] = LittleFloat(in->point[2]);
1421         }
1422 }
1423
1424 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1425 {
1426         dmodel_t        *in;
1427         dmodel_t        *out;
1428         int                     i, j, count;
1429
1430         in = (void *)(mod_base + l->fileofs);
1431         if (l->filelen % sizeof(*in))
1432                 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1433         count = l->filelen / sizeof(*in);
1434         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1435
1436         loadmodel->brushq1.submodels = out;
1437         loadmodel->brush.numsubmodels = count;
1438
1439         for ( i=0 ; i<count ; i++, in++, out++)
1440         {
1441                 for (j=0 ; j<3 ; j++)
1442                 {
1443                         // spread the mins / maxs by a pixel
1444                         out->mins[j] = LittleFloat(in->mins[j]) - 1;
1445                         out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1446                         out->origin[j] = LittleFloat(in->origin[j]);
1447                 }
1448                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1449                         out->headnode[j] = LittleLong(in->headnode[j]);
1450                 out->visleafs = LittleLong(in->visleafs);
1451                 out->firstface = LittleLong(in->firstface);
1452                 out->numfaces = LittleLong(in->numfaces);
1453         }
1454 }
1455
1456 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1457 {
1458         dedge_t *in;
1459         medge_t *out;
1460         int     i, count;
1461
1462         in = (void *)(mod_base + l->fileofs);
1463         if (l->filelen % sizeof(*in))
1464                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1465         count = l->filelen / sizeof(*in);
1466         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1467
1468         loadmodel->brushq1.edges = out;
1469         loadmodel->brushq1.numedges = count;
1470
1471         for ( i=0 ; i<count ; i++, in++, out++)
1472         {
1473                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1474                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1475         }
1476 }
1477
1478 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1479 {
1480         texinfo_t *in;
1481         mtexinfo_t *out;
1482         int i, j, k, count, miptex;
1483
1484         in = (void *)(mod_base + l->fileofs);
1485         if (l->filelen % sizeof(*in))
1486                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1487         count = l->filelen / sizeof(*in);
1488         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1489
1490         loadmodel->brushq1.texinfo = out;
1491         loadmodel->brushq1.numtexinfo = count;
1492
1493         for (i = 0;i < count;i++, in++, out++)
1494         {
1495                 for (k = 0;k < 2;k++)
1496                         for (j = 0;j < 4;j++)
1497                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1498
1499                 miptex = LittleLong(in->miptex);
1500                 out->flags = LittleLong(in->flags);
1501
1502                 out->texture = NULL;
1503                 if (loadmodel->brushq1.textures)
1504                 {
1505                         if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1506                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1507                         else
1508                                 out->texture = loadmodel->brushq1.textures + miptex;
1509                 }
1510                 if (out->flags & TEX_SPECIAL)
1511                 {
1512                         // if texture chosen is NULL or the shader needs a lightmap,
1513                         // force to notexture water shader
1514                         if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1515                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1516                 }
1517                 else
1518                 {
1519                         // if texture chosen is NULL, force to notexture
1520                         if (out->texture == NULL)
1521                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1522                 }
1523         }
1524 }
1525
1526 #if 0
1527 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1528 {
1529         int             i, j;
1530         float   *v;
1531
1532         mins[0] = mins[1] = mins[2] = 9999;
1533         maxs[0] = maxs[1] = maxs[2] = -9999;
1534         v = verts;
1535         for (i = 0;i < numverts;i++)
1536         {
1537                 for (j = 0;j < 3;j++, v++)
1538                 {
1539                         if (*v < mins[j])
1540                                 mins[j] = *v;
1541                         if (*v > maxs[j])
1542                                 maxs[j] = *v;
1543                 }
1544         }
1545 }
1546
1547 #define MAX_SUBDIVPOLYTRIANGLES 4096
1548 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1549
1550 static int subdivpolyverts, subdivpolytriangles;
1551 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1552 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1553
1554 static int subdivpolylookupvert(vec3_t v)
1555 {
1556         int i;
1557         for (i = 0;i < subdivpolyverts;i++)
1558                 if (subdivpolyvert[i][0] == v[0]
1559                  && subdivpolyvert[i][1] == v[1]
1560                  && subdivpolyvert[i][2] == v[2])
1561                         return i;
1562         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1563                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1564         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1565         return subdivpolyverts++;
1566 }
1567
1568 static void SubdividePolygon(int numverts, float *verts)
1569 {
1570         int             i, i1, i2, i3, f, b, c, p;
1571         vec3_t  mins, maxs, front[256], back[256];
1572         float   m, *pv, *cv, dist[256], frac;
1573
1574         if (numverts > 250)
1575                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1576
1577         BoundPoly(numverts, verts, mins, maxs);
1578
1579         for (i = 0;i < 3;i++)
1580         {
1581                 m = (mins[i] + maxs[i]) * 0.5;
1582                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1583                 if (maxs[i] - m < 8)
1584                         continue;
1585                 if (m - mins[i] < 8)
1586                         continue;
1587
1588                 // cut it
1589                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1590                         dist[c] = cv[i] - m;
1591
1592                 f = b = 0;
1593                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1594                 {
1595                         if (dist[p] >= 0)
1596                         {
1597                                 VectorCopy(pv, front[f]);
1598                                 f++;
1599                         }
1600                         if (dist[p] <= 0)
1601                         {
1602                                 VectorCopy(pv, back[b]);
1603                                 b++;
1604                         }
1605                         if (dist[p] == 0 || dist[c] == 0)
1606                                 continue;
1607                         if ((dist[p] > 0) != (dist[c] > 0) )
1608                         {
1609                                 // clip point
1610                                 frac = dist[p] / (dist[p] - dist[c]);
1611                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1612                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1613                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1614                                 f++;
1615                                 b++;
1616                         }
1617                 }
1618
1619                 SubdividePolygon(f, front[0]);
1620                 SubdividePolygon(b, back[0]);
1621                 return;
1622         }
1623
1624         i1 = subdivpolylookupvert(verts);
1625         i2 = subdivpolylookupvert(verts + 3);
1626         for (i = 2;i < numverts;i++)
1627         {
1628                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1629                 {
1630                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1631                         return;
1632                 }
1633
1634                 i3 = subdivpolylookupvert(verts + i * 3);
1635                 subdivpolyindex[subdivpolytriangles][0] = i1;
1636                 subdivpolyindex[subdivpolytriangles][1] = i2;
1637                 subdivpolyindex[subdivpolytriangles][2] = i3;
1638                 i2 = i3;
1639                 subdivpolytriangles++;
1640         }
1641 }
1642
1643 //Breaks a polygon up along axial 64 unit
1644 //boundaries so that turbulent and sky warps
1645 //can be done reasonably.
1646 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1647 {
1648         int i, j;
1649         surfvertex_t *v;
1650         surfmesh_t *mesh;
1651
1652         subdivpolytriangles = 0;
1653         subdivpolyverts = 0;
1654         SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1655         if (subdivpolytriangles < 1)
1656                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1657
1658         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1659         mesh->num_vertices = subdivpolyverts;
1660         mesh->num_triangles = subdivpolytriangles;
1661         mesh->vertex = (surfvertex_t *)(mesh + 1);
1662         mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1663         memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1664
1665         for (i = 0;i < mesh->num_triangles;i++)
1666                 for (j = 0;j < 3;j++)
1667                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1668
1669         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1670         {
1671                 VectorCopy(subdivpolyvert[i], v->v);
1672                 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1673                 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1674         }
1675 }
1676 #endif
1677
1678 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1679 {
1680         surfmesh_t *mesh;
1681         mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1682         mesh->num_vertices = numverts;
1683         mesh->num_triangles = numtriangles;
1684         mesh->data_vertex3f = (float *)(mesh + 1);
1685         mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1686         mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1687         mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1688         mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1689         mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1690         mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1691         mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1692         mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1693         mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1694         return mesh;
1695 }
1696
1697 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1698 {
1699         int i, lindex, j;
1700         float *vec, *vert, mins[3], maxs[3], val, *v;
1701         mtexinfo_t *tex;
1702
1703         // convert edges back to a normal polygon
1704         surf->poly_numverts = numedges;
1705         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1706         for (i = 0;i < numedges;i++)
1707         {
1708                 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1709                 if (lindex > 0)
1710                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1711                 else
1712                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1713                 VectorCopy(vec, vert);
1714                 vert += 3;
1715         }
1716
1717         // calculate polygon bounding box and center
1718         vert = surf->poly_verts;
1719         VectorCopy(vert, mins);
1720         VectorCopy(vert, maxs);
1721         vert += 3;
1722         for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1723         {
1724                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1725                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1726                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1727         }
1728         VectorCopy(mins, surf->poly_mins);
1729         VectorCopy(maxs, surf->poly_maxs);
1730         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1731         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1732         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1733
1734         // generate surface extents information
1735         tex = surf->texinfo;
1736         mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1737         mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1738         for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1739         {
1740                 for (j = 0;j < 2;j++)
1741                 {
1742                         val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1743                         if (mins[j] > val)
1744                                 mins[j] = val;
1745                         if (maxs[j] < val)
1746                                 maxs[j] = val;
1747                 }
1748         }
1749         for (i = 0;i < 2;i++)
1750         {
1751                 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1752                 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1753         }
1754 }
1755
1756 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1757 {
1758         dface_t *in;
1759         msurface_t *surf;
1760         int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1761         surfmesh_t *mesh;
1762         float s, t;
1763
1764         in = (void *)(mod_base + l->fileofs);
1765         if (l->filelen % sizeof(*in))
1766                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1767         count = l->filelen / sizeof(*in);
1768         loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1769
1770         loadmodel->brushq1.numsurfaces = count;
1771         loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1772         loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1773         loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1774
1775         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++)
1776         {
1777                 surf->number = surfnum;
1778                 // FIXME: validate edges, texinfo, etc?
1779                 firstedge = LittleLong(in->firstedge);
1780                 numedges = LittleShort(in->numedges);
1781                 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)
1782                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1783                 i = LittleShort(in->texinfo);
1784                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1785                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1786                 surf->texinfo = loadmodel->brushq1.texinfo + i;
1787                 surf->flags = surf->texinfo->texture->flags;
1788
1789                 planenum = LittleShort(in->planenum);
1790                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1791                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1792
1793                 if (LittleShort(in->side))
1794                         surf->flags |= SURF_PLANEBACK;
1795
1796                 surf->plane = loadmodel->brushq1.planes + planenum;
1797
1798                 // clear lightmap (filled in later)
1799                 surf->lightmaptexture = NULL;
1800
1801                 // force lightmap upload on first time seeing the surface
1802                 surf->cached_dlight = true;
1803
1804                 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1805
1806                 ssize = (surf->extents[0] >> 4) + 1;
1807                 tsize = (surf->extents[1] >> 4) + 1;
1808
1809                 // lighting info
1810                 for (i = 0;i < MAXLIGHTMAPS;i++)
1811                         surf->styles[i] = in->styles[i];
1812                 i = LittleLong(in->lightofs);
1813                 if (i == -1)
1814                         surf->samples = NULL;
1815                 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1816                         surf->samples = loadmodel->brushq1.lightdata + i;
1817                 else // LordHavoc: white lighting (bsp version 29)
1818                         surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1819
1820                 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1821                 {
1822                         if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1823                                 Host_Error("Bad surface extents");
1824                         // stainmap for permanent marks on walls
1825                         surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1826                         // clear to white
1827                         memset(surf->stainsamples, 255, ssize * tsize * 3);
1828                 }
1829         }
1830
1831         loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1832
1833         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++)
1834         {
1835                 mesh = &surf->mesh;
1836                 mesh->num_vertices = surf->poly_numverts;
1837                 mesh->num_triangles = surf->poly_numverts - 2;
1838                 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1839                 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1840                 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1841                 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1842                 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1843                 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1844                 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1845                 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1846                 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1847                 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1848
1849                 surf->lightmaptexturestride = 0;
1850                 surf->lightmaptexture = NULL;
1851
1852                 for (i = 0;i < mesh->num_vertices;i++)
1853                 {
1854                         mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1855                         mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1856                         mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1857                         s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1858                         t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1859                         mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1860                         mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1861                         mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1862                         mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1863                         mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1864                         mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1865                         mesh->data_lightmapoffsets[i] = 0;
1866                 }
1867
1868                 for (i = 0;i < mesh->num_triangles;i++)
1869                 {
1870                         mesh->data_element3i[i * 3 + 0] = 0;
1871                         mesh->data_element3i[i * 3 + 1] = i + 1;
1872                         mesh->data_element3i[i * 3 + 2] = i + 2;
1873                 }
1874
1875                 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1876                 Mod_BuildTextureVectorsAndNormals(mesh->num_vertices, mesh->num_triangles, mesh->data_vertex3f, mesh->data_texcoordtexture2f, mesh->data_element3i, mesh->data_svector3f, mesh->data_tvector3f, mesh->data_normal3f);
1877
1878                 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1879                 {
1880                         int i, iu, iv, smax, tmax;
1881                         float u, v, ubase, vbase, uscale, vscale;
1882
1883                         smax = surf->extents[0] >> 4;
1884                         tmax = surf->extents[1] >> 4;
1885
1886                         surf->flags |= SURF_LIGHTMAP;
1887                         if (r_miplightmaps.integer)
1888                         {
1889                                 surf->lightmaptexturestride = smax+1;
1890                                 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);
1891                         }
1892                         else
1893                         {
1894                                 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1895                                 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);
1896                         }
1897                         R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1898                         uscale = (uscale - ubase) / (smax + 1);
1899                         vscale = (vscale - vbase) / (tmax + 1);
1900
1901                         for (i = 0;i < mesh->num_vertices;i++)
1902                         {
1903                                 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1904                                 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1905                                 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1906                                 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1907                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1908                                 iu = (int) u;
1909                                 iv = (int) v;
1910                                 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1911                         }
1912                 }
1913         }
1914 }
1915
1916 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1917 {
1918         node->parent = parent;
1919         if (node->contents < 0)
1920                 return;
1921         Mod_Q1BSP_SetParent(node->children[0], node);
1922         Mod_Q1BSP_SetParent(node->children[1], node);
1923 }
1924
1925 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1926 {
1927         int                     i, j, count, p;
1928         dnode_t         *in;
1929         mnode_t         *out;
1930
1931         in = (void *)(mod_base + l->fileofs);
1932         if (l->filelen % sizeof(*in))
1933                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1934         count = l->filelen / sizeof(*in);
1935         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1936
1937         loadmodel->brushq1.nodes = out;
1938         loadmodel->brushq1.numnodes = count;
1939
1940         for ( i=0 ; i<count ; i++, in++, out++)
1941         {
1942                 for (j=0 ; j<3 ; j++)
1943                 {
1944                         out->mins[j] = LittleShort(in->mins[j]);
1945                         out->maxs[j] = LittleShort(in->maxs[j]);
1946                 }
1947
1948                 p = LittleLong(in->planenum);
1949                 out->plane = loadmodel->brushq1.planes + p;
1950
1951                 out->firstsurface = LittleShort(in->firstface);
1952                 out->numsurfaces = LittleShort(in->numfaces);
1953
1954                 for (j=0 ; j<2 ; j++)
1955                 {
1956                         p = LittleShort(in->children[j]);
1957                         if (p >= 0)
1958                                 out->children[j] = loadmodel->brushq1.nodes + p;
1959                         else
1960                                 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1961                 }
1962         }
1963
1964         Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL);    // sets nodes and leafs
1965 }
1966
1967 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1968 {
1969         dleaf_t *in;
1970         mleaf_t *out;
1971         int i, j, count, p, pvschainbytes;
1972         qbyte *pvs;
1973
1974         in = (void *)(mod_base + l->fileofs);
1975         if (l->filelen % sizeof(*in))
1976                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1977         count = l->filelen / sizeof(*in);
1978         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1979
1980         loadmodel->brushq1.leafs = out;
1981         loadmodel->brushq1.numleafs = count;
1982         // get visleafs from the submodel data
1983         pvschainbytes = (loadmodel->brushq1.submodels[0].visleafs+7)>>3;
1984         loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
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                 // FIXME: this function could really benefit from some error checking
1995
1996                 out->contents = LittleLong(in->contents);
1997
1998                 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1999                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
2000                 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
2001                 {
2002                         Con_Printf("Mod_Q1BSP_LoadLeafs: invalid marksurface range %i:%i outside range %i:%i\n", out->firstmarksurface, out->firstmarksurface + out->nummarksurfaces, 0, loadmodel->brushq1.nummarksurfaces);
2003                         out->firstmarksurface = NULL;
2004                         out->nummarksurfaces = 0;
2005                 }
2006
2007                 out->pvsdata = pvs;
2008                 memset(out->pvsdata, 0xFF, pvschainbytes);
2009                 pvs += pvschainbytes;
2010
2011                 p = LittleLong(in->visofs);
2012                 if (p >= 0 && i > 0) // ignore visofs errors on leaf 0 (solid)
2013                 {
2014                         if (p >= loadmodel->brushq1.num_compressedpvs)
2015                                 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
2016                         else
2017                                 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
2018                 }
2019
2020                 for (j = 0;j < 4;j++)
2021                         out->ambient_sound_level[j] = in->ambient_level[j];
2022
2023                 // FIXME: Insert caustics here
2024         }
2025 }
2026
2027 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
2028 {
2029         dclipnode_t *in, *out;
2030         int                     i, count;
2031         hull_t          *hull;
2032
2033         in = (void *)(mod_base + l->fileofs);
2034         if (l->filelen % sizeof(*in))
2035                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2036         count = l->filelen / sizeof(*in);
2037         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2038
2039         loadmodel->brushq1.clipnodes = out;
2040         loadmodel->brushq1.numclipnodes = count;
2041
2042         if (loadmodel->brush.ishlbsp)
2043         {
2044                 hull = &loadmodel->brushq1.hulls[1];
2045                 hull->clipnodes = out;
2046                 hull->firstclipnode = 0;
2047                 hull->lastclipnode = count-1;
2048                 hull->planes = loadmodel->brushq1.planes;
2049                 hull->clip_mins[0] = -16;
2050                 hull->clip_mins[1] = -16;
2051                 hull->clip_mins[2] = -36;
2052                 hull->clip_maxs[0] = 16;
2053                 hull->clip_maxs[1] = 16;
2054                 hull->clip_maxs[2] = 36;
2055                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2056
2057                 hull = &loadmodel->brushq1.hulls[2];
2058                 hull->clipnodes = out;
2059                 hull->firstclipnode = 0;
2060                 hull->lastclipnode = count-1;
2061                 hull->planes = loadmodel->brushq1.planes;
2062                 hull->clip_mins[0] = -32;
2063                 hull->clip_mins[1] = -32;
2064                 hull->clip_mins[2] = -32;
2065                 hull->clip_maxs[0] = 32;
2066                 hull->clip_maxs[1] = 32;
2067                 hull->clip_maxs[2] = 32;
2068                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2069
2070                 hull = &loadmodel->brushq1.hulls[3];
2071                 hull->clipnodes = out;
2072                 hull->firstclipnode = 0;
2073                 hull->lastclipnode = count-1;
2074                 hull->planes = loadmodel->brushq1.planes;
2075                 hull->clip_mins[0] = -16;
2076                 hull->clip_mins[1] = -16;
2077                 hull->clip_mins[2] = -18;
2078                 hull->clip_maxs[0] = 16;
2079                 hull->clip_maxs[1] = 16;
2080                 hull->clip_maxs[2] = 18;
2081                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2082         }
2083         else
2084         {
2085                 hull = &loadmodel->brushq1.hulls[1];
2086                 hull->clipnodes = out;
2087                 hull->firstclipnode = 0;
2088                 hull->lastclipnode = count-1;
2089                 hull->planes = loadmodel->brushq1.planes;
2090                 hull->clip_mins[0] = -16;
2091                 hull->clip_mins[1] = -16;
2092                 hull->clip_mins[2] = -24;
2093                 hull->clip_maxs[0] = 16;
2094                 hull->clip_maxs[1] = 16;
2095                 hull->clip_maxs[2] = 32;
2096                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2097
2098                 hull = &loadmodel->brushq1.hulls[2];
2099                 hull->clipnodes = out;
2100                 hull->firstclipnode = 0;
2101                 hull->lastclipnode = count-1;
2102                 hull->planes = loadmodel->brushq1.planes;
2103                 hull->clip_mins[0] = -32;
2104                 hull->clip_mins[1] = -32;
2105                 hull->clip_mins[2] = -24;
2106                 hull->clip_maxs[0] = 32;
2107                 hull->clip_maxs[1] = 32;
2108                 hull->clip_maxs[2] = 64;
2109                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2110         }
2111
2112         for (i=0 ; i<count ; i++, out++, in++)
2113         {
2114                 out->planenum = LittleLong(in->planenum);
2115                 out->children[0] = LittleShort(in->children[0]);
2116                 out->children[1] = LittleShort(in->children[1]);
2117                 if (out->children[0] >= count || out->children[1] >= count)
2118                         Host_Error("Corrupt clipping hull(out of range child)\n");
2119         }
2120 }
2121
2122 //Duplicate the drawing hull structure as a clipping hull
2123 static void Mod_Q1BSP_MakeHull0(void)
2124 {
2125         mnode_t         *in;
2126         dclipnode_t *out;
2127         int                     i;
2128         hull_t          *hull;
2129
2130         hull = &loadmodel->brushq1.hulls[0];
2131
2132         in = loadmodel->brushq1.nodes;
2133         out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
2134
2135         hull->clipnodes = out;
2136         hull->firstclipnode = 0;
2137         hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
2138         hull->planes = loadmodel->brushq1.planes;
2139
2140         for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
2141         {
2142                 out->planenum = in->plane - loadmodel->brushq1.planes;
2143                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
2144                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
2145         }
2146 }
2147
2148 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
2149 {
2150         int i, j;
2151         short *in;
2152
2153         in = (void *)(mod_base + l->fileofs);
2154         if (l->filelen % sizeof(*in))
2155                 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2156         loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2157         loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2158
2159         for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2160         {
2161                 j = (unsigned) LittleShort(in[i]);
2162                 if (j >= loadmodel->brushq1.numsurfaces)
2163                         Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2164                 loadmodel->brushq1.marksurfaces[i] = j;
2165         }
2166 }
2167
2168 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2169 {
2170         int             i;
2171         int             *in;
2172
2173         in = (void *)(mod_base + l->fileofs);
2174         if (l->filelen % sizeof(*in))
2175                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2176         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2177         loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2178
2179         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2180                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2181 }
2182
2183
2184 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2185 {
2186         int                     i;
2187         mplane_t        *out;
2188         dplane_t        *in;
2189
2190         in = (void *)(mod_base + l->fileofs);
2191         if (l->filelen % sizeof(*in))
2192                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2193
2194         loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2195         loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2196
2197         for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2198         {
2199                 out->normal[0] = LittleFloat(in->normal[0]);
2200                 out->normal[1] = LittleFloat(in->normal[1]);
2201                 out->normal[2] = LittleFloat(in->normal[2]);
2202                 out->dist = LittleFloat(in->dist);
2203
2204                 PlaneClassify(out);
2205         }
2206 }
2207
2208 typedef struct portal_s
2209 {
2210         mplane_t plane;
2211         mnode_t *nodes[2];              // [0] = front side of plane
2212         struct portal_s *next[2];
2213         winding_t *winding;
2214         struct portal_s *chain; // all portals are linked into a list
2215 }
2216 portal_t;
2217
2218 static portal_t *portalchain;
2219
2220 /*
2221 ===========
2222 AllocPortal
2223 ===========
2224 */
2225 static portal_t *AllocPortal(void)
2226 {
2227         portal_t *p;
2228         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2229         p->chain = portalchain;
2230         portalchain = p;
2231         return p;
2232 }
2233
2234 static void FreePortal(portal_t *p)
2235 {
2236         Mem_Free(p);
2237 }
2238
2239 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2240 {
2241         // calculate children first
2242         if (node->children[0]->contents >= 0)
2243                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2244         if (node->children[1]->contents >= 0)
2245                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2246
2247         // make combined bounding box from children
2248         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2249         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2250         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2251         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2252         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2253         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2254 }
2255
2256 static void Mod_Q1BSP_FinalizePortals(void)
2257 {
2258         int i, j, numportals, numpoints;
2259         portal_t *p, *pnext;
2260         mportal_t *portal;
2261         mvertex_t *point;
2262         mleaf_t *leaf, *endleaf;
2263         winding_t *w;
2264
2265         // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2266         leaf = loadmodel->brushq1.leafs;
2267         endleaf = leaf + loadmodel->brushq1.numleafs;
2268         for (;leaf < endleaf;leaf++)
2269         {
2270                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2271                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2272         }
2273         p = portalchain;
2274         while (p)
2275         {
2276                 if (p->winding)
2277                 {
2278                         for (i = 0;i < 2;i++)
2279                         {
2280                                 leaf = (mleaf_t *)p->nodes[i];
2281                                 w = p->winding;
2282                                 for (j = 0;j < w->numpoints;j++)
2283                                 {
2284                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2285                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2286                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2287                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2288                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2289                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2290                                 }
2291                         }
2292                 }
2293                 p = p->chain;
2294         }
2295
2296         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2297
2298         // tally up portal and point counts
2299         p = portalchain;
2300         numportals = 0;
2301         numpoints = 0;
2302         while (p)
2303         {
2304                 // note: this check must match the one below or it will usually corrupt memory
2305                 // 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
2306                 if (p->winding && p->nodes[0] != p->nodes[1]
2307                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2308                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2309                 {
2310                         numportals += 2;
2311                         numpoints += p->winding->numpoints * 2;
2312                 }
2313                 p = p->chain;
2314         }
2315         loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2316         loadmodel->brushq1.numportals = numportals;
2317         loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2318         loadmodel->brushq1.numportalpoints = numpoints;
2319         // clear all leaf portal chains
2320         for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2321                 loadmodel->brushq1.leafs[i].portals = NULL;
2322         // process all portals in the global portal chain, while freeing them
2323         portal = loadmodel->brushq1.portals;
2324         point = loadmodel->brushq1.portalpoints;
2325         p = portalchain;
2326         portalchain = NULL;
2327         while (p)
2328         {
2329                 pnext = p->chain;
2330
2331                 if (p->winding)
2332                 {
2333                         // note: this check must match the one above or it will usually corrupt memory
2334                         // 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
2335                         if (p->nodes[0] != p->nodes[1]
2336                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2337                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2338                         {
2339                                 // first make the back to front portal(forward portal)
2340                                 portal->points = point;
2341                                 portal->numpoints = p->winding->numpoints;
2342                                 portal->plane.dist = p->plane.dist;
2343                                 VectorCopy(p->plane.normal, portal->plane.normal);
2344                                 portal->here = (mleaf_t *)p->nodes[1];
2345                                 portal->past = (mleaf_t *)p->nodes[0];
2346                                 // copy points
2347                                 for (j = 0;j < portal->numpoints;j++)
2348                                 {
2349                                         VectorCopy(p->winding->points[j], point->position);
2350                                         point++;
2351                                 }
2352                                 PlaneClassify(&portal->plane);
2353
2354                                 // link into leaf's portal chain
2355                                 portal->next = portal->here->portals;
2356                                 portal->here->portals = portal;
2357
2358                                 // advance to next portal
2359                                 portal++;
2360
2361                                 // then make the front to back portal(backward portal)
2362                                 portal->points = point;
2363                                 portal->numpoints = p->winding->numpoints;
2364                                 portal->plane.dist = -p->plane.dist;
2365                                 VectorNegate(p->plane.normal, portal->plane.normal);
2366                                 portal->here = (mleaf_t *)p->nodes[0];
2367                                 portal->past = (mleaf_t *)p->nodes[1];
2368                                 // copy points
2369                                 for (j = portal->numpoints - 1;j >= 0;j--)
2370                                 {
2371                                         VectorCopy(p->winding->points[j], point->position);
2372                                         point++;
2373                                 }
2374                                 PlaneClassify(&portal->plane);
2375
2376                                 // link into leaf's portal chain
2377                                 portal->next = portal->here->portals;
2378                                 portal->here->portals = portal;
2379
2380                                 // advance to next portal
2381                                 portal++;
2382                         }
2383                         Winding_Free(p->winding);
2384                 }
2385                 FreePortal(p);
2386                 p = pnext;
2387         }
2388 }
2389
2390 /*
2391 =============
2392 AddPortalToNodes
2393 =============
2394 */
2395 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2396 {
2397         if (!front)
2398                 Host_Error("AddPortalToNodes: NULL front node");
2399         if (!back)
2400                 Host_Error("AddPortalToNodes: NULL back node");
2401         if (p->nodes[0] || p->nodes[1])
2402                 Host_Error("AddPortalToNodes: already included");
2403         // 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
2404
2405         p->nodes[0] = front;
2406         p->next[0] = (portal_t *)front->portals;
2407         front->portals = (mportal_t *)p;
2408
2409         p->nodes[1] = back;
2410         p->next[1] = (portal_t *)back->portals;
2411         back->portals = (mportal_t *)p;
2412 }
2413
2414 /*
2415 =============
2416 RemovePortalFromNode
2417 =============
2418 */
2419 static void RemovePortalFromNodes(portal_t *portal)
2420 {
2421         int i;
2422         mnode_t *node;
2423         void **portalpointer;
2424         portal_t *t;
2425         for (i = 0;i < 2;i++)
2426         {
2427                 node = portal->nodes[i];
2428
2429                 portalpointer = (void **) &node->portals;
2430                 while (1)
2431                 {
2432                         t = *portalpointer;
2433                         if (!t)
2434                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2435
2436                         if (t == portal)
2437                         {
2438                                 if (portal->nodes[0] == node)
2439                                 {
2440                                         *portalpointer = portal->next[0];
2441                                         portal->nodes[0] = NULL;
2442                                 }
2443                                 else if (portal->nodes[1] == node)
2444                                 {
2445                                         *portalpointer = portal->next[1];
2446                                         portal->nodes[1] = NULL;
2447                                 }
2448                                 else
2449                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2450                                 break;
2451                         }
2452
2453                         if (t->nodes[0] == node)
2454                                 portalpointer = (void **) &t->next[0];
2455                         else if (t->nodes[1] == node)
2456                                 portalpointer = (void **) &t->next[1];
2457                         else
2458                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2459                 }
2460         }
2461 }
2462
2463 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2464 {
2465         int side;
2466         mnode_t *front, *back, *other_node;
2467         mplane_t clipplane, *plane;
2468         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2469         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2470
2471         // if a leaf, we're done
2472         if (node->contents)
2473                 return;
2474
2475         plane = node->plane;
2476
2477         front = node->children[0];
2478         back = node->children[1];
2479         if (front == back)
2480                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2481
2482         // create the new portal by generating a polygon for the node plane,
2483         // and clipping it by all of the other portals(which came from nodes above this one)
2484         nodeportal = AllocPortal();
2485         nodeportal->plane = *plane;
2486
2487         nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2488         side = 0;       // shut up compiler warning
2489         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2490         {
2491                 clipplane = portal->plane;
2492                 if (portal->nodes[0] == portal->nodes[1])
2493                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2494                 if (portal->nodes[0] == node)
2495                         side = 0;
2496                 else if (portal->nodes[1] == node)
2497                 {
2498                         clipplane.dist = -clipplane.dist;
2499                         VectorNegate(clipplane.normal, clipplane.normal);
2500                         side = 1;
2501                 }
2502                 else
2503                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2504
2505                 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2506                 if (!nodeportalwinding)
2507                 {
2508                         Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2509                         break;
2510                 }
2511         }
2512
2513         if (nodeportalwinding)
2514         {
2515                 // if the plane was not clipped on all sides, there was an error
2516                 nodeportal->winding = nodeportalwinding;
2517                 AddPortalToNodes(nodeportal, front, back);
2518         }
2519
2520         // split the portals of this node along this node's plane and assign them to the children of this node
2521         // (migrating the portals downward through the tree)
2522         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2523         {
2524                 if (portal->nodes[0] == portal->nodes[1])
2525                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2526                 if (portal->nodes[0] == node)
2527                         side = 0;
2528                 else if (portal->nodes[1] == node)
2529                         side = 1;
2530                 else
2531                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2532                 nextportal = portal->next[side];
2533
2534                 other_node = portal->nodes[!side];
2535                 RemovePortalFromNodes(portal);
2536
2537                 // cut the portal into two portals, one on each side of the node plane
2538                 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2539
2540                 if (!frontwinding)
2541                 {
2542                         if (side == 0)
2543                                 AddPortalToNodes(portal, back, other_node);
2544                         else
2545                                 AddPortalToNodes(portal, other_node, back);
2546                         continue;
2547                 }
2548                 if (!backwinding)
2549                 {
2550                         if (side == 0)
2551                                 AddPortalToNodes(portal, front, other_node);
2552                         else
2553                                 AddPortalToNodes(portal, other_node, front);
2554                         continue;
2555                 }
2556
2557                 // the winding is split
2558                 splitportal = AllocPortal();
2559                 temp = splitportal->chain;
2560                 *splitportal = *portal;
2561                 splitportal->chain = temp;
2562                 splitportal->winding = backwinding;
2563                 Winding_Free(portal->winding);
2564                 portal->winding = frontwinding;
2565
2566                 if (side == 0)
2567                 {
2568                         AddPortalToNodes(portal, front, other_node);
2569                         AddPortalToNodes(splitportal, back, other_node);
2570                 }
2571                 else
2572                 {
2573                         AddPortalToNodes(portal, other_node, front);
2574                         AddPortalToNodes(splitportal, other_node, back);
2575                 }
2576         }
2577
2578         Mod_Q1BSP_RecursiveNodePortals(front);
2579         Mod_Q1BSP_RecursiveNodePortals(back);
2580 }
2581
2582 static void Mod_Q1BSP_MakePortals(void)
2583 {
2584         portalchain = NULL;
2585         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2586         Mod_Q1BSP_FinalizePortals();
2587 }
2588
2589 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2590 {
2591 #if 0
2592         int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2593         msurface_t *surf, *s;
2594         float *v0, *v1, *v2, *v3;
2595         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2596                 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2597         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2598         {
2599                 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)
2600                 {
2601                         if (surf->neighborsurfaces[vertnum])
2602                                 continue;
2603                         surf->neighborsurfaces[vertnum] = NULL;
2604                         for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2605                         {
2606                                 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2607                                  || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2608                                  || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2609                                  || s == surf)
2610                                         continue;
2611                                 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2612                                         if (s->neighborsurfaces[vnum] == surf)
2613                                                 break;
2614                                 if (vnum < s->poly_numverts)
2615                                         continue;
2616                                 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)
2617                                 {
2618                                         if (s->neighborsurfaces[vnum] == NULL
2619                                          && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2620                                           || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2621                                         {
2622                                                 surf->neighborsurfaces[vertnum] = s;
2623                                                 s->neighborsurfaces[vnum] = surf;
2624                                                 break;
2625                                         }
2626                                 }
2627                                 if (vnum < s->poly_numverts)
2628                                         break;
2629                         }
2630                 }
2631         }
2632 #endif
2633 }
2634
2635 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2636 {
2637         int i, j, stylecounts[256], totalcount, remapstyles[256];
2638         msurface_t *surf;
2639         memset(stylecounts, 0, sizeof(stylecounts));
2640         for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2641         {
2642                 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2643                 for (j = 0;j < MAXLIGHTMAPS;j++)
2644                         stylecounts[surf->styles[j]]++;
2645         }
2646         totalcount = 0;
2647         model->brushq1.light_styles = 0;
2648         for (i = 0;i < 255;i++)
2649         {
2650                 if (stylecounts[i])
2651                 {
2652                         remapstyles[i] = model->brushq1.light_styles++;
2653                         totalcount += stylecounts[i] + 1;
2654                 }
2655         }
2656         if (!totalcount)
2657                 return;
2658         model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2659         model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2660         model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2661         model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2662         model->brushq1.light_styles = 0;
2663         for (i = 0;i < 255;i++)
2664                 if (stylecounts[i])
2665                         model->brushq1.light_style[model->brushq1.light_styles++] = i;
2666         j = 0;
2667         for (i = 0;i < model->brushq1.light_styles;i++)
2668         {
2669                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2670                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2671         }
2672         for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2673         {
2674                 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2675                 for (j = 0;j < MAXLIGHTMAPS;j++)
2676                         if (surf->styles[j] != 255)
2677                                 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2678         }
2679         j = 0;
2680         for (i = 0;i < model->brushq1.light_styles;i++)
2681         {
2682                 *model->brushq1.light_styleupdatechains[i] = NULL;
2683                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2684                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2685         }
2686 }
2687
2688 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2689 {
2690         int i, j;
2691         for (i = 0;i < model->brushq1.numtextures;i++)
2692                 model->brushq1.pvstexturechainslength[i] = 0;
2693         for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2694         {
2695                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2696                 {
2697                         model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2698                         model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2699                 }
2700         }
2701         for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2702         {
2703                 if (model->brushq1.pvstexturechainslength[i])
2704                 {
2705                         model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2706                         j += model->brushq1.pvstexturechainslength[i] + 1;
2707                 }
2708                 else
2709                         model->brushq1.pvstexturechains[i] = NULL;
2710         }
2711         for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2712                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2713                         *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2714         for (i = 0;i < model->brushq1.numtextures;i++)
2715         {
2716                 if (model->brushq1.pvstexturechainslength[i])
2717                 {
2718                         *model->brushq1.pvstexturechains[i] = NULL;
2719                         model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2720                 }
2721         }
2722 }
2723
2724 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2725 {
2726         int i;
2727         float d;
2728
2729         while (node->contents >= 0)
2730         {
2731                 d = PlaneDiff(org, node->plane);
2732                 if (d > radius)
2733                         node = node->children[0];
2734                 else if (d < -radius)
2735                         node = node->children[1];
2736                 else
2737                 {
2738                         // go down both sides
2739                         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2740                         node = node->children[1];
2741                 }
2742         }
2743         // FIXME: code!
2744         // if this is a leaf, accumulate the pvs bits
2745         if (/*node->contents != CONTENTS_SOLID && */((mleaf_t *)node)->pvsdata)
2746                 for (i = 0;i < pvsbytes;i++)
2747                         pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2748 }
2749
2750 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2751 //of the given point.
2752 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2753 {
2754         int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2755         bytes = min(bytes, pvsbufferlength);
2756         if (r_novis.integer)
2757         {
2758                 memset(pvsbuffer, 0xFF, bytes);
2759                 return bytes;
2760         }
2761         memset(pvsbuffer, 0, bytes);
2762         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2763         return bytes;
2764 }
2765
2766 //Returns PVS data for a given point
2767 //(note: always returns valid data, never NULL)
2768 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2769 {
2770         mnode_t *node;
2771         Mod_CheckLoaded(model);
2772         // LordHavoc: modified to start at first clip node,
2773         // in other words: first node of the (sub)model
2774         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2775         while (node->contents == 0)
2776                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2777         return ((mleaf_t *)node)->pvsdata;
2778 }
2779
2780 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2781 {
2782         vec3_t size;
2783         const hull_t *hull;
2784
2785         VectorSubtract(inmaxs, inmins, size);
2786         if (cmodel->brush.ishlbsp)
2787         {
2788                 if (size[0] < 3)
2789                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2790                 else if (size[0] <= 32)
2791                 {
2792                         if (size[2] < 54) // pick the nearest of 36 or 72
2793                                 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2794                         else
2795                                 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2796                 }
2797                 else
2798                         hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2799         }
2800         else
2801         {
2802                 if (size[0] < 3)
2803                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2804                 else if (size[0] <= 32)
2805                         hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2806                 else
2807                         hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2808         }
2809         VectorCopy(inmins, outmins);
2810         VectorAdd(inmins, hull->clip_size, outmaxs);
2811 }
2812
2813 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2814 extern void R_Model_Brush_Draw(entity_render_t *ent);
2815 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2816 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);
2817 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2818 {
2819         int i, j, k;
2820         dheader_t *header;
2821         dmodel_t *bm;
2822         mempool_t *mainmempool;
2823         char *loadname;
2824         model_t *originalloadmodel;
2825         float dist, modelyawradius, modelradius, *vec;
2826         msurface_t *surf;
2827
2828         mod->type = mod_brush;
2829
2830         header = (dheader_t *)buffer;
2831
2832         i = LittleLong(header->version);
2833         if (i != BSPVERSION && i != 30)
2834                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2835         mod->brush.ishlbsp = i == 30;
2836
2837         mod->soundfromcenter = true;
2838         mod->TraceBox = Mod_Q1BSP_TraceBox;
2839         mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2840         mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2841         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2842         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2843         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2844         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2845         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2846         mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2847         mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2848         mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2849         mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2850
2851         if (loadmodel->isworldmodel)
2852         {
2853                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2854                 // until we get a texture for it...
2855                 R_ResetQuakeSky();
2856         }
2857
2858 // swap all the lumps
2859         mod_base = (qbyte *)header;
2860
2861         for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2862                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2863
2864 // load into heap
2865
2866         // store which lightmap format to use
2867         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2868
2869         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2870         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2871         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2872         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2873         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2874         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2875         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2876         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2877         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2878         Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2879         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2880         // load submodels before leafs because they contain the number of vis leafs
2881         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2882         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2883         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2884         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2885
2886         if (mod->brushq1.data_compressedpvs)
2887                 Mem_Free(mod->brushq1.data_compressedpvs);
2888         mod->brushq1.data_compressedpvs = NULL;
2889         mod->brushq1.num_compressedpvs = 0;
2890
2891         Mod_Q1BSP_MakeHull0();
2892         Mod_Q1BSP_MakePortals();
2893
2894         mod->numframes = 2;             // regular and alternate animation
2895
2896         mainmempool = mod->mempool;
2897         loadname = mod->name;
2898
2899         Mod_Q1BSP_LoadLightList();
2900         originalloadmodel = loadmodel;
2901
2902 //
2903 // set up the submodels(FIXME: this is confusing)
2904 //
2905         for (i = 0;i < mod->brush.numsubmodels;i++)
2906         {
2907                 bm = &mod->brushq1.submodels[i];
2908
2909                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2910                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2911                 {
2912                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2913                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2914                 }
2915
2916                 mod->brushq1.firstmodelsurface = bm->firstface;
2917                 mod->brushq1.nummodelsurfaces = bm->numfaces;
2918
2919                 // this gets altered below if sky is used
2920                 mod->DrawSky = NULL;
2921                 mod->Draw = R_Model_Brush_Draw;
2922                 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2923                 mod->DrawLight = R_Model_Brush_DrawLight;
2924                 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2925                 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2926                 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2927                 Mod_Q1BSP_BuildPVSTextureChains(mod);
2928                 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2929                 if (mod->brushq1.nummodelsurfaces)
2930                 {
2931                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2932                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2933                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2934                         modelyawradius = 0;
2935                         modelradius = 0;
2936                         for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2937                         {
2938                                 // we only need to have a drawsky function if it is used(usually only on world model)
2939                                 if (surf->texinfo->texture->shader == &Cshader_sky)
2940                                         mod->DrawSky = R_Model_Brush_DrawSky;
2941                                 // LordHavoc: submodels always clip, even if water
2942                                 if (mod->brush.numsubmodels - 1)
2943                                         surf->flags |= SURF_SOLIDCLIP;
2944                                 // calculate bounding shapes
2945                                 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
2946                                 {
2947                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2948                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2949                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2950                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2951                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2952                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2953                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
2954                                         if (modelyawradius < dist)
2955                                                 modelyawradius = dist;
2956                                         dist += vec[2]*vec[2];
2957                                         if (modelradius < dist)
2958                                                 modelradius = dist;
2959                                 }
2960                         }
2961                         modelyawradius = sqrt(modelyawradius);
2962                         modelradius = sqrt(modelradius);
2963                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2964                         mod->yawmins[2] = mod->normalmins[2];
2965                         mod->yawmaxs[2] = mod->normalmaxs[2];
2966                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2967                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2968                         mod->radius = modelradius;
2969                         mod->radius2 = modelradius * modelradius;
2970                 }
2971                 else
2972                 {
2973                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2974                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2975                 }
2976                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2977
2978                 mod->brushq1.visleafs = bm->visleafs;
2979
2980                 // LordHavoc: only register submodels if it is the world
2981                 // (prevents bsp models from replacing world submodels)
2982                 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2983                 {
2984                         char    name[10];
2985                         // duplicate the basic information
2986                         sprintf(name, "*%i", i+1);
2987                         loadmodel = Mod_FindName(name);
2988                         *loadmodel = *mod;
2989                         strcpy(loadmodel->name, name);
2990                         // textures and memory belong to the main model
2991                         loadmodel->texturepool = NULL;
2992                         loadmodel->mempool = NULL;
2993                         mod = loadmodel;
2994                 }
2995         }
2996
2997         loadmodel = originalloadmodel;
2998         //Mod_Q1BSP_ProcessLightList();
2999
3000         if (developer.integer)
3001                 Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.visleafs, loadmodel->brushq1.numportals);
3002 }
3003
3004 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3005 {
3006 }
3007
3008 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3009 {
3010 /*
3011         d_t *in;
3012         m_t *out;
3013         int i, count;
3014
3015         in = (void *)(mod_base + l->fileofs);
3016         if (l->filelen % sizeof(*in))
3017                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3018         count = l->filelen / sizeof(*in);
3019         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3020
3021         loadmodel-> = out;
3022         loadmodel->num = count;
3023
3024         for (i = 0;i < count;i++, in++, out++)
3025         {
3026         }
3027 */
3028 }
3029
3030 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3031 {
3032 /*
3033         d_t *in;
3034         m_t *out;
3035         int i, count;
3036
3037         in = (void *)(mod_base + l->fileofs);
3038         if (l->filelen % sizeof(*in))
3039                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3040         count = l->filelen / sizeof(*in);
3041         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3042
3043         loadmodel-> = out;
3044         loadmodel->num = count;
3045
3046         for (i = 0;i < count;i++, in++, out++)
3047         {
3048         }
3049 */
3050 }
3051
3052 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3053 {
3054 /*
3055         d_t *in;
3056         m_t *out;
3057         int i, count;
3058
3059         in = (void *)(mod_base + l->fileofs);
3060         if (l->filelen % sizeof(*in))
3061                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3062         count = l->filelen / sizeof(*in);
3063         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3064
3065         loadmodel-> = out;
3066         loadmodel->num = count;
3067
3068         for (i = 0;i < count;i++, in++, out++)
3069         {
3070         }
3071 */
3072 }
3073
3074 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3075 {
3076 /*
3077         d_t *in;
3078         m_t *out;
3079         int i, count;
3080
3081         in = (void *)(mod_base + l->fileofs);
3082         if (l->filelen % sizeof(*in))
3083                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3084         count = l->filelen / sizeof(*in);
3085         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3086
3087         loadmodel-> = out;
3088         loadmodel->num = count;
3089
3090         for (i = 0;i < count;i++, in++, out++)
3091         {
3092         }
3093 */
3094 }
3095
3096 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3097 {
3098 /*
3099         d_t *in;
3100         m_t *out;
3101         int i, count;
3102
3103         in = (void *)(mod_base + l->fileofs);
3104         if (l->filelen % sizeof(*in))
3105                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3106         count = l->filelen / sizeof(*in);
3107         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3108
3109         loadmodel-> = out;
3110         loadmodel->num = count;
3111
3112         for (i = 0;i < count;i++, in++, out++)
3113         {
3114         }
3115 */
3116 }
3117
3118 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3119 {
3120 /*
3121         d_t *in;
3122         m_t *out;
3123         int i, count;
3124
3125         in = (void *)(mod_base + l->fileofs);
3126         if (l->filelen % sizeof(*in))
3127                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3128         count = l->filelen / sizeof(*in);
3129         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3130
3131         loadmodel-> = out;
3132         loadmodel->num = count;
3133
3134         for (i = 0;i < count;i++, in++, out++)
3135         {
3136         }
3137 */
3138 }
3139
3140 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3141 {
3142 /*
3143         d_t *in;
3144         m_t *out;
3145         int i, count;
3146
3147         in = (void *)(mod_base + l->fileofs);
3148         if (l->filelen % sizeof(*in))
3149                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3150         count = l->filelen / sizeof(*in);
3151         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3152
3153         loadmodel-> = out;
3154         loadmodel->num = count;
3155
3156         for (i = 0;i < count;i++, in++, out++)
3157         {
3158         }
3159 */
3160 }
3161
3162 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3163 {
3164 /*
3165         d_t *in;
3166         m_t *out;
3167         int i, count;
3168
3169         in = (void *)(mod_base + l->fileofs);
3170         if (l->filelen % sizeof(*in))
3171                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3172         count = l->filelen / sizeof(*in);
3173         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3174
3175         loadmodel-> = out;
3176         loadmodel->num = count;
3177
3178         for (i = 0;i < count;i++, in++, out++)
3179         {
3180         }
3181 */
3182 }
3183
3184 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3185 {
3186 /*
3187         d_t *in;
3188         m_t *out;
3189         int i, count;
3190
3191         in = (void *)(mod_base + l->fileofs);
3192         if (l->filelen % sizeof(*in))
3193                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3194         count = l->filelen / sizeof(*in);
3195         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3196
3197         loadmodel-> = out;
3198         loadmodel->num = count;
3199
3200         for (i = 0;i < count;i++, in++, out++)
3201         {
3202         }
3203 */
3204 }
3205
3206 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3207 {
3208 /*
3209         d_t *in;
3210         m_t *out;
3211         int i, count;
3212
3213         in = (void *)(mod_base + l->fileofs);
3214         if (l->filelen % sizeof(*in))
3215                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3216         count = l->filelen / sizeof(*in);
3217         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3218
3219         loadmodel-> = out;
3220         loadmodel->num = count;
3221
3222         for (i = 0;i < count;i++, in++, out++)
3223         {
3224         }
3225 */
3226 }
3227
3228 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3229 {
3230 /*
3231         d_t *in;
3232         m_t *out;
3233         int i, count;
3234
3235         in = (void *)(mod_base + l->fileofs);
3236         if (l->filelen % sizeof(*in))
3237                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3238         count = l->filelen / sizeof(*in);
3239         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3240
3241         loadmodel-> = out;
3242         loadmodel->num = count;
3243
3244         for (i = 0;i < count;i++, in++, out++)
3245         {
3246         }
3247 */
3248 }
3249
3250 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3251 {
3252 /*
3253         d_t *in;
3254         m_t *out;
3255         int i, count;
3256
3257         in = (void *)(mod_base + l->fileofs);
3258         if (l->filelen % sizeof(*in))
3259                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3260         count = l->filelen / sizeof(*in);
3261         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3262
3263         loadmodel-> = out;
3264         loadmodel->num = count;
3265
3266         for (i = 0;i < count;i++, in++, out++)
3267         {
3268         }
3269 */
3270 }
3271
3272 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3273 {
3274 /*
3275         d_t *in;
3276         m_t *out;
3277         int i, count;
3278
3279         in = (void *)(mod_base + l->fileofs);
3280         if (l->filelen % sizeof(*in))
3281                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3282         count = l->filelen / sizeof(*in);
3283         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3284
3285         loadmodel-> = out;
3286         loadmodel->num = count;
3287
3288         for (i = 0;i < count;i++, in++, out++)
3289         {
3290         }
3291 */
3292 }
3293
3294 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3295 {
3296 /*
3297         d_t *in;
3298         m_t *out;
3299         int i, count;
3300
3301         in = (void *)(mod_base + l->fileofs);
3302         if (l->filelen % sizeof(*in))
3303                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3304         count = l->filelen / sizeof(*in);
3305         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3306
3307         loadmodel-> = out;
3308         loadmodel->num = count;
3309
3310         for (i = 0;i < count;i++, in++, out++)
3311         {
3312         }
3313 */
3314 }
3315
3316 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3317 {
3318 /*
3319         d_t *in;
3320         m_t *out;
3321         int i, count;
3322
3323         in = (void *)(mod_base + l->fileofs);
3324         if (l->filelen % sizeof(*in))
3325                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3326         count = l->filelen / sizeof(*in);
3327         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3328
3329         loadmodel-> = out;
3330         loadmodel->num = count;
3331
3332         for (i = 0;i < count;i++, in++, out++)
3333         {
3334         }
3335 */
3336 }
3337
3338 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3339 {
3340 /*
3341         d_t *in;
3342         m_t *out;
3343         int i, count;
3344
3345         in = (void *)(mod_base + l->fileofs);
3346         if (l->filelen % sizeof(*in))
3347                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3348         count = l->filelen / sizeof(*in);
3349         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3350
3351         loadmodel-> = out;
3352         loadmodel->num = count;
3353
3354         for (i = 0;i < count;i++, in++, out++)
3355         {
3356         }
3357 */
3358 }
3359
3360 static void Mod_Q2BSP_LoadModels(lump_t *l)
3361 {
3362 /*
3363         d_t *in;
3364         m_t *out;
3365         int i, count;
3366
3367         in = (void *)(mod_base + l->fileofs);
3368         if (l->filelen % sizeof(*in))
3369                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3370         count = l->filelen / sizeof(*in);
3371         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3372
3373         loadmodel-> = out;
3374         loadmodel->num = count;
3375
3376         for (i = 0;i < count;i++, in++, out++)
3377         {
3378         }
3379 */
3380 }
3381
3382 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3383 {
3384         int i;
3385         q2dheader_t *header;
3386
3387         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3388
3389         mod->type = mod_brushq2;
3390
3391         header = (q2dheader_t *)buffer;
3392
3393         i = LittleLong(header->version);
3394         if (i != Q2BSPVERSION)
3395                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3396         mod->brush.ishlbsp = false;
3397         if (loadmodel->isworldmodel)
3398         {
3399                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3400                 // until we get a texture for it...
3401                 R_ResetQuakeSky();
3402         }
3403
3404         mod_base = (qbyte *)header;
3405
3406         // swap all the lumps
3407         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3408                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3409
3410         // store which lightmap format to use
3411         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3412
3413         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3414         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3415         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3416         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3417         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3418         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3419         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3420         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3421         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3422         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3423         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3424         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3425         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3426         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3427         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3428         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3429         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3430         // LordHavoc: must go last because this makes the submodels
3431         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3432 }
3433
3434 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3435 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3436
3437 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3438 {
3439         const char *data;
3440         char key[128], value[4096];
3441         float v[3];
3442         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3443         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3444         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3445         if (!l->filelen)
3446                 return;
3447         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3448         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3449         data = loadmodel->brush.entities;
3450         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3451         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3452         {
3453                 while (1)
3454                 {
3455                         if (!COM_ParseToken(&data, false))
3456                                 break; // error
3457                         if (com_token[0] == '}')
3458                                 break; // end of worldspawn
3459                         if (com_token[0] == '_')
3460                                 strcpy(key, com_token + 1);
3461                         else
3462                                 strcpy(key, com_token);
3463                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3464                                 key[strlen(key)-1] = 0;
3465                         if (!COM_ParseToken(&data, false))
3466                                 break; // error
3467                         strcpy(value, com_token);
3468                         if (!strcmp("gridsize", key))
3469                         {
3470                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3471                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3472                         }
3473                 }
3474         }
3475 }
3476
3477 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3478 {
3479         q3dtexture_t *in;
3480         q3mtexture_t *out;
3481         int i, count;
3482         int j, c;
3483         fssearch_t *search;
3484         char *f;
3485         const char *text;
3486         int flags;
3487         char shadername[Q3PATHLENGTH];
3488
3489         in = (void *)(mod_base + l->fileofs);
3490         if (l->filelen % sizeof(*in))
3491                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3492         count = l->filelen / sizeof(*in);
3493         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3494
3495         loadmodel->brushq3.data_textures = out;
3496         loadmodel->brushq3.num_textures = count;
3497
3498         for (i = 0;i < count;i++, in++, out++)
3499         {
3500                 out->number = i;
3501                 strlcpy (out->name, in->name, sizeof (out->name));
3502                 out->surfaceflags = LittleLong(in->surfaceflags);
3503                 out->nativecontents = LittleLong(in->contents);
3504                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3505                 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3506                 out->surfaceparms = -1;
3507         }
3508
3509         // do a quick parse of shader files to get surfaceparms
3510         if ((search = FS_Search("scripts/*.shader", true, false)))
3511         {
3512                 for (i = 0;i < search->numfilenames;i++)
3513                 {
3514                         if ((f = FS_LoadFile(search->filenames[i], false)))
3515                         {
3516                                 text = f;
3517                                 while (COM_ParseToken(&text, false))
3518                                 {
3519                                         snprintf(shadername, sizeof(shadername), "%s", com_token);
3520                                         flags = 0;
3521                                         if (COM_ParseToken(&text, false) && !strcmp(com_token, "{"))
3522                                         {
3523                                                 while (COM_ParseToken(&text, false))
3524                                                 {
3525                                                         if (!strcmp(com_token, "}"))
3526                                                                 break;
3527                                                         else if (!strcmp(com_token, "{"))
3528                                                         {
3529                                                                 while (COM_ParseToken(&text, false))
3530                                                                 {
3531                                                                         if (!strcmp(com_token, "}"))
3532                                                                                 break;
3533                                                                 }
3534                                                         }
3535                                                         else if (!strcmp(com_token, "surfaceparm"))
3536                                                         {
3537                                                                 if (COM_ParseToken(&text, true) && strcmp(com_token, "\n"))
3538                                                                 {
3539                                                                         if (!strcmp(com_token, "alphashadow"))