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