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