]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
use a less bizarre method of byte swapping q1bsp/q3bsp headers
[divverent/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24 #include "polygon.h"
25 #include "curves.h"
26
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
28
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
30
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t r_subdivisions_tolerance = {0, "r_subdivisions_tolerance", "4"};
38 cvar_t r_subdivisions_minlevel = {0, "r_subdivisions_minlevel", "0"};
39 cvar_t r_subdivisions_maxlevel = {0, "r_subdivisions_maxlevel", "10"};
40 cvar_t r_subdivisions_maxvertices = {0, "r_subdivisions_maxvertices", "65536"};
41 cvar_t r_subdivisions_collision_tolerance = {0, "r_subdivisions_collision_tolerance", "15"};
42 cvar_t r_subdivisions_collision_minlevel = {0, "r_subdivisions_collision_minlevel", "0"};
43 cvar_t r_subdivisions_collision_maxlevel = {0, "r_subdivisions_collision_maxlevel", "10"};
44 cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225"};
45 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
46 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
47 cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0"};
48
49 static void Mod_Q1BSP_Collision_Init (void);
50 void Mod_BrushInit(void)
51 {
52 //      Cvar_RegisterVariable(&r_subdivide_size);
53         Cvar_RegisterVariable(&halflifebsp);
54         Cvar_RegisterVariable(&r_novis);
55         Cvar_RegisterVariable(&r_miplightmaps);
56         Cvar_RegisterVariable(&r_lightmaprgba);
57         Cvar_RegisterVariable(&r_nosurftextures);
58         Cvar_RegisterVariable(&r_subdivisions_tolerance);
59         Cvar_RegisterVariable(&r_subdivisions_minlevel);
60         Cvar_RegisterVariable(&r_subdivisions_maxlevel);
61         Cvar_RegisterVariable(&r_subdivisions_maxvertices);
62         Cvar_RegisterVariable(&r_subdivisions_collision_tolerance);
63         Cvar_RegisterVariable(&r_subdivisions_collision_minlevel);
64         Cvar_RegisterVariable(&r_subdivisions_collision_maxlevel);
65         Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
66         Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
67         Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
68         Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
69         memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
70         Mod_Q1BSP_Collision_Init();
71 }
72
73 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
74 {
75         mnode_t *node;
76
77         if (model == NULL)
78                 return NULL;
79
80         Mod_CheckLoaded(model);
81
82         // LordHavoc: modified to start at first clip node,
83         // in other words: first node of the (sub)model
84         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
85         while (node->contents == 0)
86                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
87
88         return (mleaf_t *)node;
89 }
90
91 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
92 {
93         int i;
94         mleaf_t *leaf;
95         leaf = Mod_Q1BSP_PointInLeaf(model, p);
96         if (leaf)
97         {
98                 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));
99                 if (i)
100                 {
101                         memcpy(out, leaf->ambient_sound_level, i);
102                         out += i;
103                         outsize -= i;
104                 }
105         }
106         if (outsize)
107                 memset(out, 0, outsize);
108 }
109
110 static int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
111 {
112         int clusterindex, side, nodestackindex = 0;
113         mnode_t *node, *nodestack[1024];
114         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
115         for (;;)
116         {
117                 if (node->plane)
118                 {
119                         // node - recurse down the BSP tree
120                         side = BoxOnPlaneSide(mins, maxs, node->plane) - 1;
121                         if (side < 2)
122                         {
123                                 // box is on one side of plane, take that path
124                                 node = node->children[side];
125                         }
126                         else
127                         {
128                                 // box crosses plane, take one path and remember the other
129                                 if (nodestackindex < 1024)
130                                         nodestack[nodestackindex++] = node->children[0];
131                                 node = node->children[1];
132                         }
133                 }
134                 else
135                 {
136                         // leaf - check cluster bit
137                         clusterindex = ((mleaf_t *)node)->clusterindex;
138                         if (CHECKPVSBIT(pvs, clusterindex))
139                         {
140                                 // it is visible, return immediately with the news
141                                 return true;
142                         }
143                         else
144                         {
145                                 // nothing to see here, try another path we didn't take earlier
146                                 if (nodestackindex == 0)
147                                         break;
148                                 node = nodestack[--nodestackindex];
149                         }
150                 }
151         }
152         // it is not visible
153         return false;
154 }
155
156 /*
157 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
158 {
159         mnode_t *node;
160
161         if (model == NULL)
162                 return CONTENTS_EMPTY;
163
164         Mod_CheckLoaded(model);
165
166         // LordHavoc: modified to start at first clip node,
167         // in other words: first node of the (sub)model
168         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
169         while (node->contents == 0)
170                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
171
172         return ((mleaf_t *)node)->contents;
173 }
174 */
175
176 typedef struct findnonsolidlocationinfo_s
177 {
178         vec3_t center;
179         vec_t radius;
180         vec3_t nudge;
181         vec_t bestdist;
182         model_t *model;
183 }
184 findnonsolidlocationinfo_t;
185
186 #if 0
187 extern cvar_t samelevel;
188 #endif
189 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
190 {
191         int i, surfnum, k, *tri, *mark;
192         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
193 #if 0
194         float surfnormal[3];
195 #endif
196         msurface_t *surf;
197         for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
198         {
199                 surf = info->model->brushq1.surfaces + *mark;
200                 if (surf->flags & SURF_SOLIDCLIP)
201                 {
202 #if 0
203                         VectorCopy(surf->plane->normal, surfnormal);
204                         if (surf->flags & SURF_PLANEBACK)
205                                 VectorNegate(surfnormal, surfnormal);
206 #endif
207                         for (k = 0;k < surf->mesh.num_triangles;k++)
208                         {
209                                 tri = surf->mesh.data_element3i + k * 3;
210                                 VectorCopy((surf->mesh.data_vertex3f + tri[0] * 3), vert[0]);
211                                 VectorCopy((surf->mesh.data_vertex3f + tri[1] * 3), vert[1]);
212                                 VectorCopy((surf->mesh.data_vertex3f + tri[2] * 3), vert[2]);
213                                 VectorSubtract(vert[1], vert[0], edge[0]);
214                                 VectorSubtract(vert[2], vert[1], edge[1]);
215                                 CrossProduct(edge[1], edge[0], facenormal);
216                                 if (facenormal[0] || facenormal[1] || facenormal[2])
217                                 {
218                                         VectorNormalize(facenormal);
219 #if 0
220                                         if (VectorDistance(facenormal, surfnormal) > 0.01f)
221                                                 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
222 #endif
223                                         f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
224                                         if (f <= info->bestdist && f >= -info->bestdist)
225                                         {
226                                                 VectorSubtract(vert[0], vert[2], edge[2]);
227                                                 VectorNormalize(edge[0]);
228                                                 VectorNormalize(edge[1]);
229                                                 VectorNormalize(edge[2]);
230                                                 CrossProduct(facenormal, edge[0], edgenormal[0]);
231                                                 CrossProduct(facenormal, edge[1], edgenormal[1]);
232                                                 CrossProduct(facenormal, edge[2], edgenormal[2]);
233 #if 0
234                                                 if (samelevel.integer & 1)
235                                                         VectorNegate(edgenormal[0], edgenormal[0]);
236                                                 if (samelevel.integer & 2)
237                                                         VectorNegate(edgenormal[1], edgenormal[1]);
238                                                 if (samelevel.integer & 4)
239                                                         VectorNegate(edgenormal[2], edgenormal[2]);
240                                                 for (i = 0;i < 3;i++)
241                                                         if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
242                                                          || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
243                                                          || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
244                                                                 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]);
245 #endif
246                                                 // face distance
247                                                 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
248                                                  && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
249                                                  && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
250                                                 {
251                                                         // we got lucky, the center is within the face
252                                                         dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
253                                                         if (dist < 0)
254                                                         {
255                                                                 dist = -dist;
256                                                                 if (info->bestdist > dist)
257                                                                 {
258                                                                         info->bestdist = dist;
259                                                                         VectorScale(facenormal, (info->radius - -dist), info->nudge);
260                                                                 }
261                                                         }
262                                                         else
263                                                         {
264                                                                 if (info->bestdist > dist)
265                                                                 {
266                                                                         info->bestdist = dist;
267                                                                         VectorScale(facenormal, (info->radius - dist), info->nudge);
268                                                                 }
269                                                         }
270                                                 }
271                                                 else
272                                                 {
273                                                         // check which edge or vertex the center is nearest
274                                                         for (i = 0;i < 3;i++)
275                                                         {
276                                                                 f = DotProduct(info->center, edge[i]);
277                                                                 if (f >= DotProduct(vert[0], edge[i])
278                                                                  && f <= DotProduct(vert[1], edge[i]))
279                                                                 {
280                                                                         // on edge
281                                                                         VectorMA(info->center, -f, edge[i], point);
282                                                                         dist = sqrt(DotProduct(point, point));
283                                                                         if (info->bestdist > dist)
284                                                                         {
285                                                                                 info->bestdist = dist;
286                                                                                 VectorScale(point, (info->radius / dist), info->nudge);
287                                                                         }
288                                                                         // skip both vertex checks
289                                                                         // (both are further away than this edge)
290                                                                         i++;
291                                                                 }
292                                                                 else
293                                                                 {
294                                                                         // not on edge, check first vertex of edge
295                                                                         VectorSubtract(info->center, vert[i], point);
296                                                                         dist = sqrt(DotProduct(point, point));
297                                                                         if (info->bestdist > dist)
298                                                                         {
299                                                                                 info->bestdist = dist;
300                                                                                 VectorScale(point, (info->radius / dist), info->nudge);
301                                                                         }
302                                                                 }
303                                                         }
304                                                 }
305                                         }
306                                 }
307                         }
308                 }
309         }
310 }
311
312 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
313 {
314         if (node->contents)
315         {
316                 if (((mleaf_t *)node)->nummarksurfaces)
317                         Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
318         }
319         else
320         {
321                 float f = PlaneDiff(info->center, node->plane);
322                 if (f >= -info->bestdist)
323                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
324                 if (f <= info->bestdist)
325                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
326         }
327 }
328
329 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
330 {
331         int i;
332         findnonsolidlocationinfo_t info;
333         if (model == NULL)
334         {
335                 VectorCopy(in, out);
336                 return;
337         }
338         VectorCopy(in, info.center);
339         info.radius = radius;
340         info.model = model;
341         i = 0;
342         do
343         {
344                 VectorClear(info.nudge);
345                 info.bestdist = radius;
346                 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
347                 VectorAdd(info.center, info.nudge, info.center);
348         }
349         while (info.bestdist < radius && ++i < 10);
350         VectorCopy(info.center, out);
351 }
352
353 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
354 {
355         switch(nativecontents)
356         {
357                 case CONTENTS_EMPTY:
358                         return 0;
359                 case CONTENTS_SOLID:
360                         return SUPERCONTENTS_SOLID;
361                 case CONTENTS_WATER:
362                         return SUPERCONTENTS_WATER;
363                 case CONTENTS_SLIME:
364                         return SUPERCONTENTS_SLIME;
365                 case CONTENTS_LAVA:
366                         return SUPERCONTENTS_LAVA;
367                 case CONTENTS_SKY:
368                         return SUPERCONTENTS_SKY;
369         }
370         return 0;
371 }
372
373 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
374 {
375         if (supercontents & SUPERCONTENTS_SOLID)
376                 return CONTENTS_SOLID;
377         if (supercontents & SUPERCONTENTS_SKY)
378                 return CONTENTS_SKY;
379         if (supercontents & SUPERCONTENTS_LAVA)
380                 return CONTENTS_LAVA;
381         if (supercontents & SUPERCONTENTS_SLIME)
382                 return CONTENTS_SLIME;
383         if (supercontents & SUPERCONTENTS_WATER)
384                 return CONTENTS_WATER;
385         return CONTENTS_EMPTY;
386 }
387
388 typedef struct
389 {
390         // the hull we're tracing through
391         const hull_t *hull;
392
393         // the trace structure to fill in
394         trace_t *trace;
395
396         // start, end, and end - start (in model space)
397         double start[3];
398         double end[3];
399         double dist[3];
400 }
401 RecursiveHullCheckTraceInfo_t;
402
403 // 1/32 epsilon to keep floating point happy
404 #define DIST_EPSILON (0.03125)
405
406 #define HULLCHECKSTATE_EMPTY 0
407 #define HULLCHECKSTATE_SOLID 1
408 #define HULLCHECKSTATE_DONE 2
409
410 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
411 {
412         // status variables, these don't need to be saved on the stack when
413         // recursing...  but are because this should be thread-safe
414         // (note: tracing against a bbox is not thread-safe, yet)
415         int ret;
416         mplane_t *plane;
417         double t1, t2;
418
419         // variables that need to be stored on the stack when recursing
420         dclipnode_t *node;
421         int side;
422         double midf, mid[3];
423
424         // LordHavoc: a goto!  everyone flee in terror... :)
425 loc0:
426         // check for empty
427         if (num < 0)
428         {
429                 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
430                 if (!t->trace->startfound)
431                 {
432                         t->trace->startfound = true;
433                         t->trace->startsupercontents |= num;
434                 }
435                 if (num & SUPERCONTENTS_LIQUIDSMASK)
436                         t->trace->inwater = true;
437                 if (num == 0)
438                         t->trace->inopen = true;
439                 if (num & t->trace->hitsupercontentsmask)
440                 {
441                         // if the first leaf is solid, set startsolid
442                         if (t->trace->allsolid)
443                                 t->trace->startsolid = true;
444 #if COLLISIONPARANOID >= 3
445                         Con_Print("S");
446 #endif
447                         return HULLCHECKSTATE_SOLID;
448                 }
449                 else
450                 {
451                         t->trace->allsolid = false;
452 #if COLLISIONPARANOID >= 3
453                         Con_Print("E");
454 #endif
455                         return HULLCHECKSTATE_EMPTY;
456                 }
457         }
458
459         // find the point distances
460         node = t->hull->clipnodes + num;
461
462         plane = t->hull->planes + node->planenum;
463         if (plane->type < 3)
464         {
465                 t1 = p1[plane->type] - plane->dist;
466                 t2 = p2[plane->type] - plane->dist;
467         }
468         else
469         {
470                 t1 = DotProduct (plane->normal, p1) - plane->dist;
471                 t2 = DotProduct (plane->normal, p2) - plane->dist;
472         }
473
474         if (t1 < 0)
475         {
476                 if (t2 < 0)
477                 {
478 #if COLLISIONPARANOID >= 3
479                         Con_Print("<");
480 #endif
481                         num = node->children[1];
482                         goto loc0;
483                 }
484                 side = 1;
485         }
486         else
487         {
488                 if (t2 >= 0)
489                 {
490 #if COLLISIONPARANOID >= 3
491                         Con_Print(">");
492 #endif
493                         num = node->children[0];
494                         goto loc0;
495                 }
496                 side = 0;
497         }
498
499         // the line intersects, find intersection point
500         // LordHavoc: this uses the original trace for maximum accuracy
501 #if COLLISIONPARANOID >= 3
502         Con_Print("M");
503 #endif
504         if (plane->type < 3)
505         {
506                 t1 = t->start[plane->type] - plane->dist;
507                 t2 = t->end[plane->type] - plane->dist;
508         }
509         else
510         {
511                 t1 = DotProduct (plane->normal, t->start) - plane->dist;
512                 t2 = DotProduct (plane->normal, t->end) - plane->dist;
513         }
514
515         midf = t1 / (t1 - t2);
516         midf = bound(p1f, midf, p2f);
517         VectorMA(t->start, midf, t->dist, mid);
518
519         // recurse both sides, front side first
520         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
521         // if this side is not empty, return what it is (solid or done)
522         if (ret != HULLCHECKSTATE_EMPTY)
523                 return ret;
524
525         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
526         // if other side is not solid, return what it is (empty or done)
527         if (ret != HULLCHECKSTATE_SOLID)
528                 return ret;
529
530         // front is air and back is solid, this is the impact point...
531         if (side)
532         {
533                 t->trace->plane.dist = -plane->dist;
534                 VectorNegate (plane->normal, t->trace->plane.normal);
535         }
536         else
537         {
538                 t->trace->plane.dist = plane->dist;
539                 VectorCopy (plane->normal, t->trace->plane.normal);
540         }
541
542         // calculate the true fraction
543         t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist;
544         t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist;
545         midf = t1 / (t1 - t2);
546         t->trace->realfraction = bound(0, midf, 1);
547
548         // calculate the return fraction which is nudged off the surface a bit
549         midf = (t1 - DIST_EPSILON) / (t1 - t2);
550         t->trace->fraction = bound(0, midf, 1);
551
552 #if COLLISIONPARANOID >= 3
553         Con_Print("D");
554 #endif
555         return HULLCHECKSTATE_DONE;
556 }
557
558 #if COLLISIONPARANOID < 2
559 static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
560 {
561         while (num >= 0)
562                 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];
563         num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
564         t->trace->startsupercontents |= num;
565         if (num & SUPERCONTENTS_LIQUIDSMASK)
566                 t->trace->inwater = true;
567         if (num == 0)
568                 t->trace->inopen = true;
569         if (num & t->trace->hitsupercontentsmask)
570         {
571                 t->trace->allsolid = t->trace->startsolid = true;
572                 return HULLCHECKSTATE_SOLID;
573         }
574         else
575         {
576                 t->trace->allsolid = t->trace->startsolid = false;
577                 return HULLCHECKSTATE_EMPTY;
578         }
579 }
580 #endif
581
582 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)
583 {
584         // this function currently only supports same size start and end
585         double boxsize[3];
586         RecursiveHullCheckTraceInfo_t rhc;
587
588         memset(&rhc, 0, sizeof(rhc));
589         memset(trace, 0, sizeof(trace_t));
590         rhc.trace = trace;
591         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
592         rhc.trace->fraction = 1;
593         rhc.trace->realfraction = 1;
594         rhc.trace->allsolid = true;
595         VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
596         if (boxsize[0] < 3)
597                 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
598         else if (model->brush.ishlbsp)
599         {
600                 // LordHavoc: this has to have a minor tolerance (the .1) because of
601                 // minor float precision errors from the box being transformed around
602                 if (boxsize[0] < 32.1)
603                 {
604                         if (boxsize[2] < 54) // pick the nearest of 36 or 72
605                                 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
606                         else
607                                 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
608                 }
609                 else
610                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
611         }
612         else
613         {
614                 // LordHavoc: this has to have a minor tolerance (the .1) because of
615                 // minor float precision errors from the box being transformed around
616                 if (boxsize[0] < 32.1)
617                         rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
618                 else
619                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
620         }
621         VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
622         VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
623         VectorSubtract(rhc.end, rhc.start, rhc.dist);
624 #if COLLISIONPARANOID >= 2
625         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]);
626         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
627         Con_Print("\n");
628 #else
629         if (DotProduct(rhc.dist, rhc.dist))
630                 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
631         else
632                 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
633 #endif
634 }
635
636 static hull_t box_hull;
637 static dclipnode_t box_clipnodes[6];
638 static mplane_t box_planes[6];
639
640 static void Mod_Q1BSP_Collision_Init (void)
641 {
642         int             i;
643         int             side;
644
645         //Set up the planes and clipnodes so that the six floats of a bounding box
646         //can just be stored out and get a proper hull_t structure.
647
648         box_hull.clipnodes = box_clipnodes;
649         box_hull.planes = box_planes;
650         box_hull.firstclipnode = 0;
651         box_hull.lastclipnode = 5;
652
653         for (i = 0;i < 6;i++)
654         {
655                 box_clipnodes[i].planenum = i;
656
657                 side = i&1;
658
659                 box_clipnodes[i].children[side] = CONTENTS_EMPTY;
660                 if (i != 5)
661                         box_clipnodes[i].children[side^1] = i + 1;
662                 else
663                         box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
664
665                 box_planes[i].type = i>>1;
666                 box_planes[i].normal[i>>1] = 1;
667         }
668 }
669
670 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)
671 {
672 #if 1
673         colbrushf_t cbox;
674         colplanef_t cbox_planes[6];
675         cbox.supercontents = boxsupercontents;
676         cbox.numplanes = 6;
677         cbox.numpoints = 0;
678         cbox.numtriangles = 0;
679         cbox.planes = cbox_planes;
680         cbox.points = NULL;
681         cbox.elements = NULL;
682         cbox.markframe = 0;
683         cbox.mins[0] = 0;
684         cbox.mins[1] = 0;
685         cbox.mins[2] = 0;
686         cbox.maxs[0] = 0;
687         cbox.maxs[1] = 0;
688         cbox.maxs[2] = 0;
689         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];
690         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];
691         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];
692         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];
693         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];
694         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];
695         memset(trace, 0, sizeof(trace_t));
696         trace->hitsupercontentsmask = hitsupercontentsmask;
697         trace->fraction = 1;
698         trace->realfraction = 1;
699         Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox);
700 #else
701         RecursiveHullCheckTraceInfo_t rhc;
702         // fill in a default trace
703         memset(&rhc, 0, sizeof(rhc));
704         memset(trace, 0, sizeof(trace_t));
705         //To keep everything totally uniform, bounding boxes are turned into small
706         //BSP trees instead of being compared directly.
707         // create a temp hull from bounding box sizes
708         box_planes[0].dist = cmaxs[0] - mins[0];
709         box_planes[1].dist = cmins[0] - maxs[0];
710         box_planes[2].dist = cmaxs[1] - mins[1];
711         box_planes[3].dist = cmins[1] - maxs[1];
712         box_planes[4].dist = cmaxs[2] - mins[2];
713         box_planes[5].dist = cmins[2] - maxs[2];
714 #if COLLISIONPARANOID >= 3
715         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]);
716 #endif
717         // trace a line through the generated clipping hull
718         //rhc.boxsupercontents = boxsupercontents;
719         rhc.hull = &box_hull;
720         rhc.trace = trace;
721         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
722         rhc.trace->fraction = 1;
723         rhc.trace->realfraction = 1;
724         rhc.trace->allsolid = true;
725         VectorCopy(start, rhc.start);
726         VectorCopy(end, rhc.end);
727         VectorSubtract(rhc.end, rhc.start, rhc.dist);
728         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
729         //VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
730         if (rhc.trace->startsupercontents)
731                 rhc.trace->startsupercontents = boxsupercontents;
732 #endif
733 }
734
735 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)
736 {
737         int side, distz = endz - startz;
738         float front, back;
739         float mid;
740
741 loc0:
742         if (node->contents < 0)
743                 return false;           // didn't hit anything
744
745         switch (node->plane->type)
746         {
747         case PLANE_X:
748                 node = node->children[x < node->plane->dist];
749                 goto loc0;
750         case PLANE_Y:
751                 node = node->children[y < node->plane->dist];
752                 goto loc0;
753         case PLANE_Z:
754                 side = startz < node->plane->dist;
755                 if ((endz < node->plane->dist) == side)
756                 {
757                         node = node->children[side];
758                         goto loc0;
759                 }
760                 // found an intersection
761                 mid = node->plane->dist;
762                 break;
763         default:
764                 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
765                 front += startz * node->plane->normal[2];
766                 back += endz * node->plane->normal[2];
767                 side = front < node->plane->dist;
768                 if ((back < node->plane->dist) == side)
769                 {
770                         node = node->children[side];
771                         goto loc0;
772                 }
773                 // found an intersection
774                 mid = startz + distz * (front - node->plane->dist) / (front - back);
775                 break;
776         }
777
778         // go down front side
779         if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
780                 return true;    // hit something
781         else
782         {
783                 // check for impact on this node
784                 if (node->numsurfaces)
785                 {
786                         int i, ds, dt;
787                         msurface_t *surf;
788
789                         surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
790                         for (i = 0;i < node->numsurfaces;i++, surf++)
791                         {
792                                 if (!(surf->flags & SURF_LIGHTMAP) || !surf->samples)
793                                         continue;       // no lightmaps
794
795                                 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];
796                                 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];
797
798                                 if (ds >= 0 && ds < surf->extents[0] && dt >= 0 && dt < surf->extents[1])
799                                 {
800                                         qbyte *lightmap;
801                                         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;
802                                         lmwidth = ((surf->extents[0]>>4)+1);
803                                         lmheight = ((surf->extents[1]>>4)+1);
804                                         line3 = lmwidth * 3; // LordHavoc: *3 for colored lighting
805                                         size3 = lmwidth * lmheight * 3; // LordHavoc: *3 for colored lighting
806
807                                         lightmap = surf->samples + ((dt>>4) * lmwidth + (ds>>4))*3; // LordHavoc: *3 for colored lighting
808
809                                         for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
810                                         {
811                                                 scale = d_lightstylevalue[surf->styles[maps]];
812                                                 r00 += lightmap[      0] * scale;g00 += lightmap[      1] * scale;b00 += lightmap[      2] * scale;
813                                                 r01 += lightmap[      3] * scale;g01 += lightmap[      4] * scale;b01 += lightmap[      5] * scale;
814                                                 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
815                                                 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
816                                                 lightmap += size3;
817                                         }
818
819 /*
820 LordHavoc: here's the readable version of the interpolation
821 code, not quite as easy for the compiler to optimize...
822
823 dsfrac is the X position in the lightmap pixel, * 16
824 dtfrac is the Y position in the lightmap pixel, * 16
825 r00 is top left corner, r01 is top right corner
826 r10 is bottom left corner, r11 is bottom right corner
827 g and b are the same layout.
828 r0 and r1 are the top and bottom intermediate results
829
830 first we interpolate the top two points, to get the top
831 edge sample
832
833         r0 = (((r01-r00) * dsfrac) >> 4) + r00;
834         g0 = (((g01-g00) * dsfrac) >> 4) + g00;
835         b0 = (((b01-b00) * dsfrac) >> 4) + b00;
836
837 then we interpolate the bottom two points, to get the
838 bottom edge sample
839
840         r1 = (((r11-r10) * dsfrac) >> 4) + r10;
841         g1 = (((g11-g10) * dsfrac) >> 4) + g10;
842         b1 = (((b11-b10) * dsfrac) >> 4) + b10;
843
844 then we interpolate the top and bottom samples to get the
845 middle sample (the one which was requested)
846
847         r = (((r1-r0) * dtfrac) >> 4) + r0;
848         g = (((g1-g0) * dtfrac) >> 4) + g0;
849         b = (((b1-b0) * dtfrac) >> 4) + b0;
850 */
851
852                                         ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
853                                         ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
854                                         ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
855                                         return true; // success
856                                 }
857                         }
858                 }
859
860                 // go down back side
861                 node = node->children[side ^ 1];
862                 startz = mid;
863                 distz = endz - startz;
864                 goto loc0;
865         }
866 }
867
868 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
869 {
870         Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, cl.worldmodel->brushq1.nodes + cl.worldmodel->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
871 }
872
873 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
874 {
875         int c;
876         qbyte *outstart = out;
877         while (out < outend)
878         {
879                 if (in == inend)
880                 {
881                         Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
882                         return;
883                 }
884                 c = *in++;
885                 if (c)
886                         *out++ = c;
887                 else
888                 {
889                         if (in == inend)
890                         {
891                                 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun (during zero-run) on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
892                                 return;
893                         }
894                         for (c = *in++;c > 0;c--)
895                         {
896                                 if (out == outend)
897                                 {
898                                         Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
899                                         return;
900                                 }
901                                 *out++ = 0;
902                         }
903                 }
904         }
905 }
906
907 static void Mod_Q1BSP_LoadTextures(lump_t *l)
908 {
909         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
910         miptex_t *dmiptex;
911         texture_t *tx, *tx2, *anims[10], *altanims[10];
912         dmiptexlump_t *m;
913         qbyte *data, *mtdata;
914         char name[256];
915
916         loadmodel->brushq1.textures = NULL;
917
918         // add two slots for notexture walls and notexture liquids
919         if (l->filelen)
920         {
921                 m = (dmiptexlump_t *)(mod_base + l->fileofs);
922                 m->nummiptex = LittleLong (m->nummiptex);
923                 loadmodel->brushq1.numtextures = m->nummiptex + 2;
924         }
925         else
926         {
927                 m = NULL;
928                 loadmodel->brushq1.numtextures = 2;
929         }
930
931         loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
932
933         // fill out all slots with notexture
934         for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
935         {
936                 tx->number = i;
937                 strcpy(tx->name, "NO TEXTURE FOUND");
938                 tx->width = 16;
939                 tx->height = 16;
940                 tx->skin.base = r_notexture;
941                 if (i == loadmodel->brushq1.numtextures - 1)
942                         tx->flags = SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
943                 else
944                         tx->flags = SURF_LIGHTMAP | SURF_SOLIDCLIP;
945                 tx->currentframe = tx;
946         }
947
948         if (!m)
949                 return;
950
951         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
952         dofs = m->dataofs;
953         // LordHavoc: mostly rewritten map texture loader
954         for (i = 0;i < m->nummiptex;i++)
955         {
956                 dofs[i] = LittleLong(dofs[i]);
957                 if (dofs[i] == -1 || r_nosurftextures.integer)
958                         continue;
959                 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
960
961                 // make sure name is no more than 15 characters
962                 for (j = 0;dmiptex->name[j] && j < 15;j++)
963                         name[j] = dmiptex->name[j];
964                 name[j] = 0;
965
966                 mtwidth = LittleLong(dmiptex->width);
967                 mtheight = LittleLong(dmiptex->height);
968                 mtdata = NULL;
969                 j = LittleLong(dmiptex->offsets[0]);
970                 if (j)
971                 {
972                         // texture included
973                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
974                         {
975                                 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
976                                 continue;
977                         }
978                         mtdata = (qbyte *)dmiptex + j;
979                 }
980
981                 if ((mtwidth & 15) || (mtheight & 15))
982                         Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned\n", dmiptex->name, loadmodel->name);
983
984                 // LordHavoc: force all names to lowercase
985                 for (j = 0;name[j];j++)
986                         if (name[j] >= 'A' && name[j] <= 'Z')
987                                 name[j] += 'a' - 'A';
988
989                 tx = loadmodel->brushq1.textures + i;
990                 strcpy(tx->name, name);
991                 tx->width = mtwidth;
992                 tx->height = mtheight;
993
994                 if (!tx->name[0])
995                 {
996                         sprintf(tx->name, "unnamed%i", i);
997                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
998                 }
999
1000                 // LordHavoc: HL sky textures are entirely different than quake
1001                 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
1002                 {
1003                         if (loadmodel->isworldmodel)
1004                         {
1005                                 data = loadimagepixels(tx->name, false, 0, 0);
1006                                 if (data)
1007                                 {
1008                                         if (image_width == 256 && image_height == 128)
1009                                         {
1010                                                 R_InitSky(data, 4);
1011                                                 Mem_Free(data);
1012                                         }
1013                                         else
1014                                         {
1015                                                 Mem_Free(data);
1016                                                 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
1017                                                 if (mtdata != NULL)
1018                                                         R_InitSky(mtdata, 1);
1019                                         }
1020                                 }
1021                                 else if (mtdata != NULL)
1022                                         R_InitSky(mtdata, 1);
1023                         }
1024                 }
1025                 else
1026                 {
1027                         if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true, true))
1028                         {
1029                                 // did not find external texture, load it from the bsp or wad3
1030                                 if (loadmodel->brush.ishlbsp)
1031                                 {
1032                                         // internal texture overrides wad
1033                                         qbyte *pixels, *freepixels, *fogpixels;
1034                                         pixels = freepixels = NULL;
1035                                         if (mtdata)
1036                                                 pixels = W_ConvertWAD3Texture(dmiptex);
1037                                         if (pixels == NULL)
1038                                                 pixels = freepixels = W_GetTexture(tx->name);
1039                                         if (pixels != NULL)
1040                                         {
1041                                                 tx->width = image_width;
1042                                                 tx->height = image_height;
1043                                                 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);
1044                                                 if (Image_CheckAlpha(pixels, image_width * image_height, true))
1045                                                 {
1046                                                         fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
1047                                                         for (j = 0;j < image_width * image_height * 4;j += 4)
1048                                                         {
1049                                                                 fogpixels[j + 0] = 255;
1050                                                                 fogpixels[j + 1] = 255;
1051                                                                 fogpixels[j + 2] = 255;
1052                                                                 fogpixels[j + 3] = pixels[j + 3];
1053                                                         }
1054                                                         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);
1055                                                         Mem_Free(fogpixels);
1056                                                 }
1057                                         }
1058                                         if (freepixels)
1059                                                 Mem_Free(freepixels);
1060                                 }
1061                                 else if (mtdata) // texture included
1062                                         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);
1063                         }
1064                 }
1065                 if (tx->skin.base == NULL)
1066                 {
1067                         // no texture found
1068                         tx->width = 16;
1069                         tx->height = 16;
1070                         tx->skin.base = r_notexture;
1071                 }
1072
1073                 if (tx->name[0] == '*')
1074                 {
1075                         // turb does not block movement
1076                         tx->flags = SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
1077                         // LordHavoc: some turbulent textures should be fullbright and solid
1078                         if (!strncmp(tx->name,"*lava",5)
1079                          || !strncmp(tx->name,"*teleport",9)
1080                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
1081                                 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
1082                         else
1083                                 tx->flags |= SURF_WATERALPHA;
1084                 }
1085                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
1086                         tx->flags = SURF_DRAWSKY | SURF_SOLIDCLIP;
1087                 else
1088                         tx->flags = SURF_LIGHTMAP | SURF_SOLIDCLIP;
1089
1090                 // start out with no animation
1091                 tx->currentframe = tx;
1092         }
1093
1094         // sequence the animations
1095         for (i = 0;i < m->nummiptex;i++)
1096         {
1097                 tx = loadmodel->brushq1.textures + i;
1098                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
1099                         continue;
1100                 if (tx->anim_total[0] || tx->anim_total[1])
1101                         continue;       // already sequenced
1102
1103                 // find the number of frames in the animation
1104                 memset(anims, 0, sizeof(anims));
1105                 memset(altanims, 0, sizeof(altanims));
1106
1107                 for (j = i;j < m->nummiptex;j++)
1108                 {
1109                         tx2 = loadmodel->brushq1.textures + j;
1110                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
1111                                 continue;
1112
1113                         num = tx2->name[1];
1114                         if (num >= '0' && num <= '9')
1115                                 anims[num - '0'] = tx2;
1116                         else if (num >= 'a' && num <= 'j')
1117                                 altanims[num - 'a'] = tx2;
1118                         else
1119                                 Con_Printf("Bad animating texture %s\n", tx->name);
1120                 }
1121
1122                 max = altmax = 0;
1123                 for (j = 0;j < 10;j++)
1124                 {
1125                         if (anims[j])
1126                                 max = j + 1;
1127                         if (altanims[j])
1128                                 altmax = j + 1;
1129                 }
1130                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
1131
1132                 incomplete = false;
1133                 for (j = 0;j < max;j++)
1134                 {
1135                         if (!anims[j])
1136                         {
1137                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
1138                                 incomplete = true;
1139                         }
1140                 }
1141                 for (j = 0;j < altmax;j++)
1142                 {
1143                         if (!altanims[j])
1144                         {
1145                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
1146                                 incomplete = true;
1147                         }
1148                 }
1149                 if (incomplete)
1150                         continue;
1151
1152                 if (altmax < 1)
1153                 {
1154                         // if there is no alternate animation, duplicate the primary
1155                         // animation into the alternate
1156                         altmax = max;
1157                         for (k = 0;k < 10;k++)
1158                                 altanims[k] = anims[k];
1159                 }
1160
1161                 // link together the primary animation
1162                 for (j = 0;j < max;j++)
1163                 {
1164                         tx2 = anims[j];
1165                         tx2->animated = true;
1166                         tx2->anim_total[0] = max;
1167                         tx2->anim_total[1] = altmax;
1168                         for (k = 0;k < 10;k++)
1169                         {
1170                                 tx2->anim_frames[0][k] = anims[k];
1171                                 tx2->anim_frames[1][k] = altanims[k];
1172                         }
1173                 }
1174
1175                 // if there really is an alternate anim...
1176                 if (anims[0] != altanims[0])
1177                 {
1178                         // link together the alternate animation
1179                         for (j = 0;j < altmax;j++)
1180                         {
1181                                 tx2 = altanims[j];
1182                                 tx2->animated = true;
1183                                 // the primary/alternate are reversed here
1184                                 tx2->anim_total[0] = altmax;
1185                                 tx2->anim_total[1] = max;
1186                                 for (k = 0;k < 10;k++)
1187                                 {
1188                                         tx2->anim_frames[0][k] = altanims[k];
1189                                         tx2->anim_frames[1][k] = anims[k];
1190                                 }
1191                         }
1192                 }
1193         }
1194 }
1195
1196 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1197 {
1198         int i;
1199         qbyte *in, *out, *data, d;
1200         char litfilename[1024];
1201         loadmodel->brushq1.lightdata = NULL;
1202         if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1203         {
1204                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1205                 for (i=0; i<l->filelen; i++)
1206                         loadmodel->brushq1.lightdata[i] = mod_base[l->fileofs+i] >>= 1;
1207         }
1208         else // LordHavoc: bsp version 29 (normal white lighting)
1209         {
1210                 // LordHavoc: hope is not lost yet, check for a .lit file to load
1211                 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1212                 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1213                 strlcat (litfilename, ".lit", sizeof (litfilename));
1214                 data = (qbyte*) FS_LoadFile(litfilename, tempmempool, false);
1215                 if (data)
1216                 {
1217                         if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1218                         {
1219                                 i = LittleLong(((int *)data)[1]);
1220                                 if (i == 1)
1221                                 {
1222                                         Con_DPrintf("loaded %s\n", litfilename);
1223                                         loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1224                                         memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1225                                         Mem_Free(data);
1226                                         return;
1227                                 }
1228                                 else
1229                                 {
1230                                         Con_Printf("Unknown .lit file version (%d)\n", i);
1231                                         Mem_Free(data);
1232                                 }
1233                         }
1234                         else
1235                         {
1236                                 if (fs_filesize == 8)
1237                                         Con_Print("Empty .lit file, ignoring\n");
1238                                 else
1239                                         Con_Print("Corrupt .lit file (old version?), ignoring\n");
1240                                 Mem_Free(data);
1241                         }
1242                 }
1243                 // LordHavoc: oh well, expand the white lighting data
1244                 if (!l->filelen)
1245                         return;
1246                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1247                 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1248                 out = loadmodel->brushq1.lightdata;
1249                 memcpy(in, mod_base + l->fileofs, l->filelen);
1250                 for (i = 0;i < l->filelen;i++)
1251                 {
1252                         d = *in++;
1253                         *out++ = d;
1254                         *out++ = d;
1255                         *out++ = d;
1256                 }
1257         }
1258 }
1259
1260 static void Mod_Q1BSP_LoadLightList(void)
1261 {
1262         int a, n, numlights;
1263         char lightsfilename[1024], *s, *t, *lightsstring;
1264         mlight_t *e;
1265
1266         strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1267         FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1268         strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1269         s = lightsstring = (char *) FS_LoadFile(lightsfilename, tempmempool, false);
1270         if (s)
1271         {
1272                 numlights = 0;
1273                 while (*s)
1274                 {
1275                         while (*s && *s != '\n')
1276                                 s++;
1277                         if (!*s)
1278                         {
1279                                 Mem_Free(lightsstring);
1280                                 Host_Error("lights file must end with a newline\n");
1281                         }
1282                         s++;
1283                         numlights++;
1284                 }
1285                 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1286                 s = lightsstring;
1287                 n = 0;
1288                 while (*s && n < numlights)
1289                 {
1290                         t = s;
1291                         while (*s && *s != '\n')
1292                                 s++;
1293                         if (!*s)
1294                         {
1295                                 Mem_Free(lightsstring);
1296                                 Host_Error("misparsed lights file!\n");
1297                         }
1298                         e = loadmodel->brushq1.lights + n;
1299                         *s = 0;
1300                         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);
1301                         *s = '\n';
1302                         if (a != 14)
1303                         {
1304                                 Mem_Free(lightsstring);
1305                                 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
1306                         }
1307                         s++;
1308                         n++;
1309                 }
1310                 if (*s)
1311                 {
1312                         Mem_Free(lightsstring);
1313                         Host_Error("misparsed lights file!\n");
1314                 }
1315                 loadmodel->brushq1.numlights = numlights;
1316                 Mem_Free(lightsstring);
1317         }
1318 }
1319
1320 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1321 {
1322         loadmodel->brushq1.num_compressedpvs = 0;
1323         loadmodel->brushq1.data_compressedpvs = NULL;
1324         if (!l->filelen)
1325                 return;
1326         loadmodel->brushq1.num_compressedpvs = l->filelen;
1327         loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1328         memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1329 }
1330
1331 // used only for HalfLife maps
1332 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1333 {
1334         char key[128], value[4096];
1335         char wadname[128];
1336         int i, j, k;
1337         if (!data)
1338                 return;
1339         if (!COM_ParseToken(&data, false))
1340                 return; // error
1341         if (com_token[0] != '{')
1342                 return; // error
1343         while (1)
1344         {
1345                 if (!COM_ParseToken(&data, false))
1346                         return; // error
1347                 if (com_token[0] == '}')
1348                         break; // end of worldspawn
1349                 if (com_token[0] == '_')
1350                         strcpy(key, com_token + 1);
1351                 else
1352                         strcpy(key, com_token);
1353                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1354                         key[strlen(key)-1] = 0;
1355                 if (!COM_ParseToken(&data, false))
1356                         return; // error
1357                 strcpy(value, com_token);
1358                 if (!strcmp("wad", key)) // for HalfLife maps
1359                 {
1360                         if (loadmodel->brush.ishlbsp)
1361                         {
1362                                 j = 0;
1363                                 for (i = 0;i < 4096;i++)
1364                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1365                                                 break;
1366                                 if (value[i])
1367                                 {
1368                                         for (;i < 4096;i++)
1369                                         {
1370                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1371                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1372                                                         j = i+1;
1373                                                 else if (value[i] == ';' || value[i] == 0)
1374                                                 {
1375                                                         k = value[i];
1376                                                         value[i] = 0;
1377                                                         strcpy(wadname, "textures/");
1378                                                         strcat(wadname, &value[j]);
1379                                                         W_LoadTextureWadFile(wadname, false);
1380                                                         j = i+1;
1381                                                         if (!k)
1382                                                                 break;
1383                                                 }
1384                                         }
1385                                 }
1386                         }
1387                 }
1388         }
1389 }
1390
1391 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1392 {
1393         loadmodel->brush.entities = NULL;
1394         if (!l->filelen)
1395                 return;
1396         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1397         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1398         if (loadmodel->brush.ishlbsp)
1399                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1400 }
1401
1402
1403 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1404 {
1405         dvertex_t       *in;
1406         mvertex_t       *out;
1407         int                     i, count;
1408
1409         in = (void *)(mod_base + l->fileofs);
1410         if (l->filelen % sizeof(*in))
1411                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1412         count = l->filelen / sizeof(*in);
1413         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1414
1415         loadmodel->brushq1.vertexes = out;
1416         loadmodel->brushq1.numvertexes = count;
1417
1418         for ( i=0 ; i<count ; i++, in++, out++)
1419         {
1420                 out->position[0] = LittleFloat(in->point[0]);
1421                 out->position[1] = LittleFloat(in->point[1]);
1422                 out->position[2] = LittleFloat(in->point[2]);
1423         }
1424 }
1425
1426 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1427 {
1428         dmodel_t        *in;
1429         dmodel_t        *out;
1430         int                     i, j, count;
1431
1432         in = (void *)(mod_base + l->fileofs);
1433         if (l->filelen % sizeof(*in))
1434                 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1435         count = l->filelen / sizeof(*in);
1436         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1437
1438         loadmodel->brushq1.submodels = out;
1439         loadmodel->brush.numsubmodels = count;
1440
1441         for ( i=0 ; i<count ; i++, in++, out++)
1442         {
1443                 for (j=0 ; j<3 ; j++)
1444                 {
1445                         // spread the mins / maxs by a pixel
1446                         out->mins[j] = LittleFloat(in->mins[j]) - 1;
1447                         out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1448                         out->origin[j] = LittleFloat(in->origin[j]);
1449                 }
1450                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1451                         out->headnode[j] = LittleLong(in->headnode[j]);
1452                 out->visleafs = LittleLong(in->visleafs);
1453                 out->firstface = LittleLong(in->firstface);
1454                 out->numfaces = LittleLong(in->numfaces);
1455         }
1456 }
1457
1458 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1459 {
1460         dedge_t *in;
1461         medge_t *out;
1462         int     i, count;
1463
1464         in = (void *)(mod_base + l->fileofs);
1465         if (l->filelen % sizeof(*in))
1466                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1467         count = l->filelen / sizeof(*in);
1468         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1469
1470         loadmodel->brushq1.edges = out;
1471         loadmodel->brushq1.numedges = count;
1472
1473         for ( i=0 ; i<count ; i++, in++, out++)
1474         {
1475                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1476                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1477         }
1478 }
1479
1480 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1481 {
1482         texinfo_t *in;
1483         mtexinfo_t *out;
1484         int i, j, k, count, miptex;
1485
1486         in = (void *)(mod_base + l->fileofs);
1487         if (l->filelen % sizeof(*in))
1488                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1489         count = l->filelen / sizeof(*in);
1490         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1491
1492         loadmodel->brushq1.texinfo = out;
1493         loadmodel->brushq1.numtexinfo = count;
1494
1495         for (i = 0;i < count;i++, in++, out++)
1496         {
1497                 for (k = 0;k < 2;k++)
1498                         for (j = 0;j < 4;j++)
1499                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1500
1501                 miptex = LittleLong(in->miptex);
1502                 out->flags = LittleLong(in->flags);
1503
1504                 out->texture = NULL;
1505                 if (loadmodel->brushq1.textures)
1506                 {
1507                         if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1508                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1509                         else
1510                                 out->texture = loadmodel->brushq1.textures + miptex;
1511                 }
1512                 if (out->flags & TEX_SPECIAL)
1513                 {
1514                         // if texture chosen is NULL or the shader needs a lightmap,
1515                         // force to notexture water shader
1516                         if (out->texture == NULL || out->texture->flags & SURF_LIGHTMAP)
1517                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1518                 }
1519                 else
1520                 {
1521                         // if texture chosen is NULL, force to notexture
1522                         if (out->texture == NULL)
1523                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1524                 }
1525         }
1526 }
1527
1528 #if 0
1529 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1530 {
1531         int             i, j;
1532         float   *v;
1533
1534         mins[0] = mins[1] = mins[2] = 9999;
1535         maxs[0] = maxs[1] = maxs[2] = -9999;
1536         v = verts;
1537         for (i = 0;i < numverts;i++)
1538         {
1539                 for (j = 0;j < 3;j++, v++)
1540                 {
1541                         if (*v < mins[j])
1542                                 mins[j] = *v;
1543                         if (*v > maxs[j])
1544                                 maxs[j] = *v;
1545                 }
1546         }
1547 }
1548
1549 #define MAX_SUBDIVPOLYTRIANGLES 4096
1550 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1551
1552 static int subdivpolyverts, subdivpolytriangles;
1553 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1554 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1555
1556 static int subdivpolylookupvert(vec3_t v)
1557 {
1558         int i;
1559         for (i = 0;i < subdivpolyverts;i++)
1560                 if (subdivpolyvert[i][0] == v[0]
1561                  && subdivpolyvert[i][1] == v[1]
1562                  && subdivpolyvert[i][2] == v[2])
1563                         return i;
1564         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1565                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1566         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1567         return subdivpolyverts++;
1568 }
1569
1570 static void SubdividePolygon(int numverts, float *verts)
1571 {
1572         int             i, i1, i2, i3, f, b, c, p;
1573         vec3_t  mins, maxs, front[256], back[256];
1574         float   m, *pv, *cv, dist[256], frac;
1575
1576         if (numverts > 250)
1577                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1578
1579         BoundPoly(numverts, verts, mins, maxs);
1580
1581         for (i = 0;i < 3;i++)
1582         {
1583                 m = (mins[i] + maxs[i]) * 0.5;
1584                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1585                 if (maxs[i] - m < 8)
1586                         continue;
1587                 if (m - mins[i] < 8)
1588                         continue;
1589
1590                 // cut it
1591                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1592                         dist[c] = cv[i] - m;
1593
1594                 f = b = 0;
1595                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1596                 {
1597                         if (dist[p] >= 0)
1598                         {
1599                                 VectorCopy(pv, front[f]);
1600                                 f++;
1601                         }
1602                         if (dist[p] <= 0)
1603                         {
1604                                 VectorCopy(pv, back[b]);
1605                                 b++;
1606                         }
1607                         if (dist[p] == 0 || dist[c] == 0)
1608                                 continue;
1609                         if ((dist[p] > 0) != (dist[c] > 0) )
1610                         {
1611                                 // clip point
1612                                 frac = dist[p] / (dist[p] - dist[c]);
1613                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1614                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1615                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1616                                 f++;
1617                                 b++;
1618                         }
1619                 }
1620
1621                 SubdividePolygon(f, front[0]);
1622                 SubdividePolygon(b, back[0]);
1623                 return;
1624         }
1625
1626         i1 = subdivpolylookupvert(verts);
1627         i2 = subdivpolylookupvert(verts + 3);
1628         for (i = 2;i < numverts;i++)
1629         {
1630                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1631                 {
1632                         Con_Print("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1633                         return;
1634                 }
1635
1636                 i3 = subdivpolylookupvert(verts + i * 3);
1637                 subdivpolyindex[subdivpolytriangles][0] = i1;
1638                 subdivpolyindex[subdivpolytriangles][1] = i2;
1639                 subdivpolyindex[subdivpolytriangles][2] = i3;
1640                 i2 = i3;
1641                 subdivpolytriangles++;
1642         }
1643 }
1644
1645 //Breaks a polygon up along axial 64 unit
1646 //boundaries so that turbulent and sky warps
1647 //can be done reasonably.
1648 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1649 {
1650         int i, j;
1651         surfvertex_t *v;
1652         surfmesh_t *mesh;
1653
1654         subdivpolytriangles = 0;
1655         subdivpolyverts = 0;
1656         SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1657         if (subdivpolytriangles < 1)
1658                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1659
1660         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1661         mesh->num_vertices = subdivpolyverts;
1662         mesh->num_triangles = subdivpolytriangles;
1663         mesh->vertex = (surfvertex_t *)(mesh + 1);
1664         mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1665         memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1666
1667         for (i = 0;i < mesh->num_triangles;i++)
1668                 for (j = 0;j < 3;j++)
1669                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1670
1671         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1672         {
1673                 VectorCopy(subdivpolyvert[i], v->v);
1674                 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1675                 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1676         }
1677 }
1678 #endif
1679
1680 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1681 {
1682         surfmesh_t *mesh;
1683         mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1684         mesh->num_vertices = numverts;
1685         mesh->num_triangles = numtriangles;
1686         mesh->data_vertex3f = (float *)(mesh + 1);
1687         mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1688         mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1689         mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1690         mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1691         mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1692         mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1693         mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1694         mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1695         mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1696         return mesh;
1697 }
1698
1699 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1700 {
1701         int i, lindex, j;
1702         float *vec, *vert, mins[3], maxs[3], val, *v;
1703         mtexinfo_t *tex;
1704
1705         // convert edges back to a normal polygon
1706         surf->poly_numverts = numedges;
1707         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1708         for (i = 0;i < numedges;i++)
1709         {
1710                 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1711                 if (lindex > 0)
1712                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1713                 else
1714                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1715                 VectorCopy(vec, vert);
1716                 vert += 3;
1717         }
1718
1719         // calculate polygon bounding box and center
1720         vert = surf->poly_verts;
1721         VectorCopy(vert, mins);
1722         VectorCopy(vert, maxs);
1723         vert += 3;
1724         for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1725         {
1726                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1727                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1728                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1729         }
1730         VectorCopy(mins, surf->poly_mins);
1731         VectorCopy(maxs, surf->poly_maxs);
1732         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1733         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1734         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1735
1736         // generate surface extents information
1737         tex = surf->texinfo;
1738         mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1739         mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1740         for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1741         {
1742                 for (j = 0;j < 2;j++)
1743                 {
1744                         val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1745                         if (mins[j] > val)
1746                                 mins[j] = val;
1747                         if (maxs[j] < val)
1748                                 maxs[j] = val;
1749                 }
1750         }
1751         for (i = 0;i < 2;i++)
1752         {
1753                 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1754                 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1755         }
1756 }
1757
1758 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1759 {
1760         dface_t *in;
1761         msurface_t *surf;
1762         int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1763         surfmesh_t *mesh;
1764         float s, t;
1765
1766         in = (void *)(mod_base + l->fileofs);
1767         if (l->filelen % sizeof(*in))
1768                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1769         count = l->filelen / sizeof(*in);
1770         loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1771
1772         loadmodel->brushq1.numsurfaces = count;
1773         loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1774         loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1775         loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1776
1777         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++)
1778         {
1779                 surf->number = surfnum;
1780                 // FIXME: validate edges, texinfo, etc?
1781                 firstedge = LittleLong(in->firstedge);
1782                 numedges = LittleShort(in->numedges);
1783                 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)
1784                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1785                 i = LittleShort(in->texinfo);
1786                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1787                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1788                 surf->texinfo = loadmodel->brushq1.texinfo + i;
1789                 surf->flags = surf->texinfo->texture->flags;
1790
1791                 planenum = LittleShort(in->planenum);
1792                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1793                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1794
1795                 if (LittleShort(in->side))
1796                         surf->flags |= SURF_PLANEBACK;
1797
1798                 surf->plane = loadmodel->brushq1.planes + planenum;
1799
1800                 // clear lightmap (filled in later)
1801                 surf->lightmaptexture = NULL;
1802
1803                 // force lightmap upload on first time seeing the surface
1804                 surf->cached_dlight = true;
1805
1806                 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1807
1808                 ssize = (surf->extents[0] >> 4) + 1;
1809                 tsize = (surf->extents[1] >> 4) + 1;
1810
1811                 // lighting info
1812                 for (i = 0;i < MAXLIGHTMAPS;i++)
1813                         surf->styles[i] = in->styles[i];
1814                 i = LittleLong(in->lightofs);
1815                 if (i == -1)
1816                         surf->samples = NULL;
1817                 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1818                         surf->samples = loadmodel->brushq1.lightdata + i;
1819                 else // LordHavoc: white lighting (bsp version 29)
1820                         surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1821
1822                 if (surf->texinfo->texture->flags & SURF_LIGHTMAP)
1823                 {
1824                         if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1825                                 Host_Error("Bad surface extents");
1826                         // stainmap for permanent marks on walls
1827                         surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1828                         // clear to white
1829                         memset(surf->stainsamples, 255, ssize * tsize * 3);
1830                 }
1831         }
1832
1833         loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1834
1835         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++)
1836         {
1837                 mesh = &surf->mesh;
1838                 mesh->num_vertices = surf->poly_numverts;
1839                 mesh->num_triangles = surf->poly_numverts - 2;
1840                 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1841                 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1842                 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1843                 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1844                 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1845                 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1846                 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1847                 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1848                 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1849                 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1850
1851                 surf->lightmaptexturestride = 0;
1852                 surf->lightmaptexture = NULL;
1853
1854                 for (i = 0;i < mesh->num_vertices;i++)
1855                 {
1856                         mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1857                         mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1858                         mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1859                         s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1860                         t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1861                         mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1862                         mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1863                         mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1864                         mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1865                         mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1866                         mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1867                         mesh->data_lightmapoffsets[i] = 0;
1868                 }
1869
1870                 for (i = 0;i < mesh->num_triangles;i++)
1871                 {
1872                         mesh->data_element3i[i * 3 + 0] = 0;
1873                         mesh->data_element3i[i * 3 + 1] = i + 1;
1874                         mesh->data_element3i[i * 3 + 2] = i + 2;
1875                 }
1876
1877                 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1878                 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);
1879
1880                 if (surf->texinfo->texture->flags & SURF_LIGHTMAP)
1881                 {
1882                         int i, iu, iv, smax, tmax;
1883                         float u, v, ubase, vbase, uscale, vscale;
1884
1885                         smax = surf->extents[0] >> 4;
1886                         tmax = surf->extents[1] >> 4;
1887
1888                         if (r_miplightmaps.integer)
1889                         {
1890                                 surf->lightmaptexturestride = smax+1;
1891                                 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);
1892                         }
1893                         else
1894                         {
1895                                 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1896                                 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);
1897                         }
1898                         R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1899                         uscale = (uscale - ubase) / (smax + 1);
1900                         vscale = (vscale - vbase) / (tmax + 1);
1901
1902                         for (i = 0;i < mesh->num_vertices;i++)
1903                         {
1904                                 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1905                                 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1906                                 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1907                                 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1908                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1909                                 iu = (int) u;
1910                                 iv = (int) v;
1911                                 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1912                         }
1913                 }
1914         }
1915 }
1916
1917 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1918 {
1919         node->parent = parent;
1920         if (node->contents < 0)
1921                 return;
1922         Mod_Q1BSP_SetParent(node->children[0], node);
1923         Mod_Q1BSP_SetParent(node->children[1], node);
1924 }
1925
1926 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1927 {
1928         int                     i, j, count, p;
1929         dnode_t         *in;
1930         mnode_t         *out;
1931
1932         in = (void *)(mod_base + l->fileofs);
1933         if (l->filelen % sizeof(*in))
1934                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1935         count = l->filelen / sizeof(*in);
1936         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1937
1938         loadmodel->brushq1.nodes = out;
1939         loadmodel->brushq1.numnodes = count;
1940
1941         for ( i=0 ; i<count ; i++, in++, out++)
1942         {
1943                 for (j=0 ; j<3 ; j++)
1944                 {
1945                         out->mins[j] = LittleShort(in->mins[j]);
1946                         out->maxs[j] = LittleShort(in->maxs[j]);
1947                 }
1948
1949                 p = LittleLong(in->planenum);
1950                 out->plane = loadmodel->brushq1.planes + p;
1951
1952                 out->firstsurface = LittleShort(in->firstface);
1953                 out->numsurfaces = LittleShort(in->numfaces);
1954
1955                 for (j=0 ; j<2 ; j++)
1956                 {
1957                         p = LittleShort(in->children[j]);
1958                         if (p >= 0)
1959                                 out->children[j] = loadmodel->brushq1.nodes + p;
1960                         else
1961                                 out->children[j] = (mnode_t *)(loadmodel->brushq1.data_leafs + (-1 - p));
1962                 }
1963         }
1964
1965         Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL);    // sets nodes and leafs
1966 }
1967
1968 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1969 {
1970         dleaf_t *in;
1971         mleaf_t *out;
1972         int i, j, count, p;
1973
1974         in = (void *)(mod_base + l->fileofs);
1975         if (l->filelen % sizeof(*in))
1976                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1977         count = l->filelen / sizeof(*in);
1978         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1979
1980         loadmodel->brushq1.data_leafs = out;
1981         loadmodel->brushq1.num_leafs = count;
1982         // get visleafs from the submodel data
1983         loadmodel->brush.num_pvsclusters = loadmodel->brushq1.submodels[0].visleafs;
1984         loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters+7)>>3;
1985         loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
1986         memset(loadmodel->brush.data_pvsclusters, 0xFF, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
1987
1988         for ( i=0 ; i<count ; i++, in++, out++)
1989         {
1990                 for (j=0 ; j<3 ; j++)
1991                 {
1992                         out->mins[j] = LittleShort(in->mins[j]);
1993                         out->maxs[j] = LittleShort(in->maxs[j]);
1994                 }
1995
1996                 // FIXME: this function could really benefit from some error checking
1997
1998                 out->contents = LittleLong(in->contents);
1999
2000                 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
2001                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
2002                 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
2003                 {
2004                         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);
2005                         out->firstmarksurface = NULL;
2006                         out->nummarksurfaces = 0;
2007                 }
2008
2009                 out->clusterindex = i - 1;
2010                 if (out->clusterindex >= loadmodel->brush.num_pvsclusters)
2011                         out->clusterindex = -1;
2012
2013                 p = LittleLong(in->visofs);
2014                 // ignore visofs errors on leaf 0 (solid)
2015                 if (p >= 0 && out->clusterindex >= 0)
2016                 {
2017                         if (p >= loadmodel->brushq1.num_compressedpvs)
2018                                 Con_Print("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
2019                         else
2020                                 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);
2021                 }
2022
2023                 for (j = 0;j < 4;j++)
2024                         out->ambient_sound_level[j] = in->ambient_level[j];
2025
2026                 // FIXME: Insert caustics here
2027         }
2028 }
2029
2030 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
2031 {
2032         dclipnode_t *in, *out;
2033         int                     i, count;
2034         hull_t          *hull;
2035
2036         in = (void *)(mod_base + l->fileofs);
2037         if (l->filelen % sizeof(*in))
2038                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2039         count = l->filelen / sizeof(*in);
2040         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2041
2042         loadmodel->brushq1.clipnodes = out;
2043         loadmodel->brushq1.numclipnodes = count;
2044
2045         if (loadmodel->brush.ishlbsp)
2046         {
2047                 hull = &loadmodel->brushq1.hulls[1];
2048                 hull->clipnodes = out;
2049                 hull->firstclipnode = 0;
2050                 hull->lastclipnode = count-1;
2051                 hull->planes = loadmodel->brushq1.planes;
2052                 hull->clip_mins[0] = -16;
2053                 hull->clip_mins[1] = -16;
2054                 hull->clip_mins[2] = -36;
2055                 hull->clip_maxs[0] = 16;
2056                 hull->clip_maxs[1] = 16;
2057                 hull->clip_maxs[2] = 36;
2058                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2059
2060                 hull = &loadmodel->brushq1.hulls[2];
2061                 hull->clipnodes = out;
2062                 hull->firstclipnode = 0;
2063                 hull->lastclipnode = count-1;
2064                 hull->planes = loadmodel->brushq1.planes;
2065                 hull->clip_mins[0] = -32;
2066                 hull->clip_mins[1] = -32;
2067                 hull->clip_mins[2] = -32;
2068                 hull->clip_maxs[0] = 32;
2069                 hull->clip_maxs[1] = 32;
2070                 hull->clip_maxs[2] = 32;
2071                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2072
2073                 hull = &loadmodel->brushq1.hulls[3];
2074                 hull->clipnodes = out;
2075                 hull->firstclipnode = 0;
2076                 hull->lastclipnode = count-1;
2077                 hull->planes = loadmodel->brushq1.planes;
2078                 hull->clip_mins[0] = -16;
2079                 hull->clip_mins[1] = -16;
2080                 hull->clip_mins[2] = -18;
2081                 hull->clip_maxs[0] = 16;
2082                 hull->clip_maxs[1] = 16;
2083                 hull->clip_maxs[2] = 18;
2084                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2085         }
2086         else
2087         {
2088                 hull = &loadmodel->brushq1.hulls[1];
2089                 hull->clipnodes = out;
2090                 hull->firstclipnode = 0;
2091                 hull->lastclipnode = count-1;
2092                 hull->planes = loadmodel->brushq1.planes;
2093                 hull->clip_mins[0] = -16;
2094                 hull->clip_mins[1] = -16;
2095                 hull->clip_mins[2] = -24;
2096                 hull->clip_maxs[0] = 16;
2097                 hull->clip_maxs[1] = 16;
2098                 hull->clip_maxs[2] = 32;
2099                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2100
2101                 hull = &loadmodel->brushq1.hulls[2];
2102                 hull->clipnodes = out;
2103                 hull->firstclipnode = 0;
2104                 hull->lastclipnode = count-1;
2105                 hull->planes = loadmodel->brushq1.planes;
2106                 hull->clip_mins[0] = -32;
2107                 hull->clip_mins[1] = -32;
2108                 hull->clip_mins[2] = -24;
2109                 hull->clip_maxs[0] = 32;
2110                 hull->clip_maxs[1] = 32;
2111                 hull->clip_maxs[2] = 64;
2112                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2113         }
2114
2115         for (i=0 ; i<count ; i++, out++, in++)
2116         {
2117                 out->planenum = LittleLong(in->planenum);
2118                 out->children[0] = LittleShort(in->children[0]);
2119                 out->children[1] = LittleShort(in->children[1]);
2120                 if (out->children[0] >= count || out->children[1] >= count)
2121                         Host_Error("Corrupt clipping hull(out of range child)\n");
2122         }
2123 }
2124
2125 //Duplicate the drawing hull structure as a clipping hull
2126 static void Mod_Q1BSP_MakeHull0(void)
2127 {
2128         mnode_t         *in;
2129         dclipnode_t *out;
2130         int                     i;
2131         hull_t          *hull;
2132
2133         hull = &loadmodel->brushq1.hulls[0];
2134
2135         in = loadmodel->brushq1.nodes;
2136         out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
2137
2138         hull->clipnodes = out;
2139         hull->firstclipnode = 0;
2140         hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
2141         hull->planes = loadmodel->brushq1.planes;
2142
2143         for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
2144         {
2145                 out->planenum = in->plane - loadmodel->brushq1.planes;
2146                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
2147                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
2148         }
2149 }
2150
2151 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
2152 {
2153         int i, j;
2154         short *in;
2155
2156         in = (void *)(mod_base + l->fileofs);
2157         if (l->filelen % sizeof(*in))
2158                 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2159         loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2160         loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2161
2162         for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2163         {
2164                 j = (unsigned) LittleShort(in[i]);
2165                 if (j >= loadmodel->brushq1.numsurfaces)
2166                         Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2167                 loadmodel->brushq1.marksurfaces[i] = j;
2168         }
2169 }
2170
2171 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2172 {
2173         int             i;
2174         int             *in;
2175
2176         in = (void *)(mod_base + l->fileofs);
2177         if (l->filelen % sizeof(*in))
2178                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2179         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2180         loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2181
2182         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2183                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2184 }
2185
2186
2187 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2188 {
2189         int                     i;
2190         mplane_t        *out;
2191         dplane_t        *in;
2192
2193         in = (void *)(mod_base + l->fileofs);
2194         if (l->filelen % sizeof(*in))
2195                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2196
2197         loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2198         loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2199
2200         for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2201         {
2202                 out->normal[0] = LittleFloat(in->normal[0]);
2203                 out->normal[1] = LittleFloat(in->normal[1]);
2204                 out->normal[2] = LittleFloat(in->normal[2]);
2205                 out->dist = LittleFloat(in->dist);
2206
2207                 PlaneClassify(out);
2208         }
2209 }
2210
2211 #define MAX_PORTALPOINTS 64
2212
2213 typedef struct portal_s
2214 {
2215         mplane_t plane;
2216         mnode_t *nodes[2];              // [0] = front side of plane
2217         struct portal_s *next[2];
2218         int numpoints;
2219         double points[3*MAX_PORTALPOINTS];
2220         struct portal_s *chain; // all portals are linked into a list
2221 }
2222 portal_t;
2223
2224 static portal_t *portalchain;
2225
2226 /*
2227 ===========
2228 AllocPortal
2229 ===========
2230 */
2231 static portal_t *AllocPortal(void)
2232 {
2233         portal_t *p;
2234         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2235         p->chain = portalchain;
2236         portalchain = p;
2237         return p;
2238 }
2239
2240 static void FreePortal(portal_t *p)
2241 {
2242         Mem_Free(p);
2243 }
2244
2245 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2246 {
2247         // calculate children first
2248         if (node->children[0]->contents >= 0)
2249                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2250         if (node->children[1]->contents >= 0)
2251                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2252
2253         // make combined bounding box from children
2254         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2255         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2256         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2257         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2258         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2259         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2260 }
2261
2262 static void Mod_Q1BSP_FinalizePortals(void)
2263 {
2264         int i, j, numportals, numpoints;
2265         portal_t *p, *pnext;
2266         mportal_t *portal;
2267         mvertex_t *point;
2268         mleaf_t *leaf, *endleaf;
2269
2270         // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2271         leaf = loadmodel->brushq1.data_leafs;
2272         endleaf = leaf + loadmodel->brushq1.num_leafs;
2273         for (;leaf < endleaf;leaf++)
2274         {
2275                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2276                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2277         }
2278         p = portalchain;
2279         while (p)
2280         {
2281                 if (p->numpoints >= 3)
2282                 {
2283                         for (i = 0;i < 2;i++)
2284                         {
2285                                 leaf = (mleaf_t *)p->nodes[i];
2286                                 for (j = 0;j < p->numpoints;j++)
2287                                 {
2288                                         if (leaf->mins[0] > p->points[j*3+0]) leaf->mins[0] = p->points[j*3+0];
2289                                         if (leaf->mins[1] > p->points[j*3+1]) leaf->mins[1] = p->points[j*3+1];
2290                                         if (leaf->mins[2] > p->points[j*3+2]) leaf->mins[2] = p->points[j*3+2];
2291                                         if (leaf->maxs[0] < p->points[j*3+0]) leaf->maxs[0] = p->points[j*3+0];
2292                                         if (leaf->maxs[1] < p->points[j*3+1]) leaf->maxs[1] = p->points[j*3+1];
2293                                         if (leaf->maxs[2] < p->points[j*3+2]) leaf->maxs[2] = p->points[j*3+2];
2294                                 }
2295                         }
2296                 }
2297                 p = p->chain;
2298         }
2299
2300         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2301
2302         // tally up portal and point counts
2303         p = portalchain;
2304         numportals = 0;
2305         numpoints = 0;
2306         while (p)
2307         {
2308                 // note: this check must match the one below or it will usually corrupt memory
2309                 // 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
2310                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1]
2311                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2312                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2313                 {
2314                         numportals += 2;
2315                         numpoints += p->numpoints * 2;
2316                 }
2317                 p = p->chain;
2318         }
2319         loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2320         loadmodel->brushq1.numportals = numportals;
2321         loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2322         loadmodel->brushq1.numportalpoints = numpoints;
2323         // clear all leaf portal chains
2324         for (i = 0;i < loadmodel->brushq1.num_leafs;i++)
2325                 loadmodel->brushq1.data_leafs[i].portals = NULL;
2326         // process all portals in the global portal chain, while freeing them
2327         portal = loadmodel->brushq1.portals;
2328         point = loadmodel->brushq1.portalpoints;
2329         p = portalchain;
2330         portalchain = NULL;
2331         while (p)
2332         {
2333                 pnext = p->chain;
2334
2335                 if (p->numpoints >= 3)
2336                 {
2337                         // note: this check must match the one above or it will usually corrupt memory
2338                         // 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
2339                         if (p->nodes[0] != p->nodes[1]
2340                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2341                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2342                         {
2343                                 // first make the back to front portal(forward portal)
2344                                 portal->points = point;
2345                                 portal->numpoints = p->numpoints;
2346                                 portal->plane.dist = p->plane.dist;
2347                                 VectorCopy(p->plane.normal, portal->plane.normal);
2348                                 portal->here = (mleaf_t *)p->nodes[1];
2349                                 portal->past = (mleaf_t *)p->nodes[0];
2350                                 // copy points
2351                                 for (j = 0;j < portal->numpoints;j++)
2352                                 {
2353                                         VectorCopy(p->points + j*3, point->position);
2354                                         point++;
2355                                 }
2356                                 PlaneClassify(&portal->plane);
2357
2358                                 // link into leaf's portal chain
2359                                 portal->next = portal->here->portals;
2360                                 portal->here->portals = portal;
2361
2362                                 // advance to next portal
2363                                 portal++;
2364
2365                                 // then make the front to back portal(backward portal)
2366                                 portal->points = point;
2367                                 portal->numpoints = p->numpoints;
2368                                 portal->plane.dist = -p->plane.dist;
2369                                 VectorNegate(p->plane.normal, portal->plane.normal);
2370                                 portal->here = (mleaf_t *)p->nodes[0];
2371                                 portal->past = (mleaf_t *)p->nodes[1];
2372                                 // copy points
2373                                 for (j = portal->numpoints - 1;j >= 0;j--)
2374                                 {
2375                                         VectorCopy(p->points + j*3, point->position);
2376                                         point++;
2377                                 }
2378                                 PlaneClassify(&portal->plane);
2379
2380                                 // link into leaf's portal chain
2381                                 portal->next = portal->here->portals;
2382                                 portal->here->portals = portal;
2383
2384                                 // advance to next portal
2385                                 portal++;
2386                         }
2387                 }
2388                 FreePortal(p);
2389                 p = pnext;
2390         }
2391 }
2392
2393 /*
2394 =============
2395 AddPortalToNodes
2396 =============
2397 */
2398 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2399 {
2400         if (!front)
2401                 Host_Error("AddPortalToNodes: NULL front node");
2402         if (!back)
2403                 Host_Error("AddPortalToNodes: NULL back node");
2404         if (p->nodes[0] || p->nodes[1])
2405                 Host_Error("AddPortalToNodes: already included");
2406         // 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
2407
2408         p->nodes[0] = front;
2409         p->next[0] = (portal_t *)front->portals;
2410         front->portals = (mportal_t *)p;
2411
2412         p->nodes[1] = back;
2413         p->next[1] = (portal_t *)back->portals;
2414         back->portals = (mportal_t *)p;
2415 }
2416
2417 /*
2418 =============
2419 RemovePortalFromNode
2420 =============
2421 */
2422 static void RemovePortalFromNodes(portal_t *portal)
2423 {
2424         int i;
2425         mnode_t *node;
2426         void **portalpointer;
2427         portal_t *t;
2428         for (i = 0;i < 2;i++)
2429         {
2430                 node = portal->nodes[i];
2431
2432                 portalpointer = (void **) &node->portals;
2433                 while (1)
2434                 {
2435                         t = *portalpointer;
2436                         if (!t)
2437                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2438
2439                         if (t == portal)
2440                         {
2441                                 if (portal->nodes[0] == node)
2442                                 {
2443                                         *portalpointer = portal->next[0];
2444                                         portal->nodes[0] = NULL;
2445                                 }
2446                                 else if (portal->nodes[1] == node)
2447                                 {
2448                                         *portalpointer = portal->next[1];
2449                                         portal->nodes[1] = NULL;
2450                                 }
2451                                 else
2452                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2453                                 break;
2454                         }
2455
2456                         if (t->nodes[0] == node)
2457                                 portalpointer = (void **) &t->next[0];
2458                         else if (t->nodes[1] == node)
2459                                 portalpointer = (void **) &t->next[1];
2460                         else
2461                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2462                 }
2463         }
2464 }
2465
2466 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2467 {
2468         int i, side;
2469         mnode_t *front, *back, *other_node;
2470         mplane_t clipplane, *plane;
2471         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2472         int numfrontpoints, numbackpoints;
2473         double frontpoints[3*MAX_PORTALPOINTS], backpoints[3*MAX_PORTALPOINTS];
2474
2475         // if a leaf, we're done
2476         if (node->contents)
2477                 return;
2478
2479         plane = node->plane;
2480
2481         front = node->children[0];
2482         back = node->children[1];
2483         if (front == back)
2484                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2485
2486         // create the new portal by generating a polygon for the node plane,
2487         // and clipping it by all of the other portals(which came from nodes above this one)
2488         nodeportal = AllocPortal();
2489         nodeportal->plane = *plane;
2490
2491         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);
2492         nodeportal->numpoints = 4;
2493         side = 0;       // shut up compiler warning
2494         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2495         {
2496                 clipplane = portal->plane;
2497                 if (portal->nodes[0] == portal->nodes[1])
2498                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2499                 if (portal->nodes[0] == node)
2500                         side = 0;
2501                 else if (portal->nodes[1] == node)
2502                 {
2503                         clipplane.dist = -clipplane.dist;
2504                         VectorNegate(clipplane.normal, clipplane.normal);
2505                         side = 1;
2506                 }
2507                 else
2508                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2509
2510                 for (i = 0;i < nodeportal->numpoints*3;i++)
2511                         frontpoints[i] = nodeportal->points[i];
2512                 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);
2513                 if (nodeportal->numpoints <= 0 || nodeportal->numpoints >= MAX_PORTALPOINTS)
2514                         break;
2515         }
2516
2517         if (nodeportal->numpoints < 3)
2518         {
2519                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2520                 nodeportal->numpoints = 0;
2521         }
2522         else if (nodeportal->numpoints >= MAX_PORTALPOINTS)
2523         {
2524                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal has too many points\n");
2525                 nodeportal->numpoints = 0;
2526         }
2527         else
2528         {
2529                 AddPortalToNodes(nodeportal, front, back);
2530
2531                 // split the portals of this node along this node's plane and assign them to the children of this node
2532                 // (migrating the portals downward through the tree)
2533                 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2534                 {
2535                         if (portal->nodes[0] == portal->nodes[1])
2536                                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2537                         if (portal->nodes[0] == node)
2538                                 side = 0;
2539                         else if (portal->nodes[1] == node)
2540                                 side = 1;
2541                         else
2542                                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2543                         nextportal = portal->next[side];
2544
2545                         other_node = portal->nodes[!side];
2546                         RemovePortalFromNodes(portal);
2547
2548                         // cut the portal into two portals, one on each side of the node plane
2549                         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); 
2550
2551                         if (!numfrontpoints)
2552                         {
2553                                 if (side == 0)
2554                                         AddPortalToNodes(portal, back, other_node);
2555                                 else
2556                                         AddPortalToNodes(portal, other_node, back);
2557                                 continue;
2558                         }
2559                         if (!numbackpoints)
2560                         {
2561                                 if (side == 0)
2562                                         AddPortalToNodes(portal, front, other_node);
2563                                 else
2564                                         AddPortalToNodes(portal, other_node, front);
2565                                 continue;
2566                         }
2567
2568                         // the portal is split
2569                         splitportal = AllocPortal();
2570                         temp = splitportal->chain;
2571                         *splitportal = *portal;
2572                         splitportal->chain = temp;
2573                         for (i = 0;i < numbackpoints*3;i++)
2574                                 splitportal->points[i] = backpoints[i];
2575                         splitportal->numpoints = numbackpoints;
2576                         for (i = 0;i < numfrontpoints*3;i++)
2577                                 portal->points[i] = frontpoints[i];
2578                         portal->numpoints = numfrontpoints;
2579
2580                         if (side == 0)
2581                         {
2582                                 AddPortalToNodes(portal, front, other_node);
2583                                 AddPortalToNodes(splitportal, back, other_node);
2584                         }
2585                         else
2586                         {
2587                                 AddPortalToNodes(portal, other_node, front);
2588                                 AddPortalToNodes(splitportal, other_node, back);
2589                         }
2590                 }
2591         }
2592
2593         Mod_Q1BSP_RecursiveNodePortals(front);
2594         Mod_Q1BSP_RecursiveNodePortals(back);
2595 }
2596
2597 static void Mod_Q1BSP_MakePortals(void)
2598 {
2599         portalchain = NULL;
2600         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2601         Mod_Q1BSP_FinalizePortals();
2602 }
2603
2604 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2605 {
2606 #if 0
2607         int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2608         msurface_t *surf, *s;
2609         float *v0, *v1, *v2, *v3;
2610         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2611                 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2612         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2613         {
2614                 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)
2615                 {
2616                         if (surf->neighborsurfaces[vertnum])
2617                                 continue;
2618                         surf->neighborsurfaces[vertnum] = NULL;
2619                         for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2620                         {
2621                                 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2622                                  || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2623                                  || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2624                                  || s == surf)
2625                                         continue;
2626                                 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2627                                         if (s->neighborsurfaces[vnum] == surf)
2628                                                 break;
2629                                 if (vnum < s->poly_numverts)
2630                                         continue;
2631                                 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)
2632                                 {
2633                                         if (s->neighborsurfaces[vnum] == NULL
2634                                          && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2635                                           || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2636                                         {
2637                                                 surf->neighborsurfaces[vertnum] = s;
2638                                                 s->neighborsurfaces[vnum] = surf;
2639                                                 break;
2640                                         }
2641                                 }
2642                                 if (vnum < s->poly_numverts)
2643                                         break;
2644                         }
2645                 }
2646         }
2647 #endif
2648 }
2649
2650 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2651 {
2652         int i, j, stylecounts[256], totalcount, remapstyles[256];
2653         msurface_t *surf;
2654         memset(stylecounts, 0, sizeof(stylecounts));
2655         for (i = 0;i < model->nummodelsurfaces;i++)
2656         {
2657                 surf = model->brushq1.surfaces + model->firstmodelsurface + i;
2658                 for (j = 0;j < MAXLIGHTMAPS;j++)
2659                         stylecounts[surf->styles[j]]++;
2660         }
2661         totalcount = 0;
2662         model->brushq1.light_styles = 0;
2663         for (i = 0;i < 255;i++)
2664         {
2665                 if (stylecounts[i])
2666                 {
2667                         remapstyles[i] = model->brushq1.light_styles++;
2668                         totalcount += stylecounts[i] + 1;
2669                 }
2670         }
2671         if (!totalcount)
2672                 return;
2673         model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2674         model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2675         model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2676         model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2677         model->brushq1.light_styles = 0;
2678         for (i = 0;i < 255;i++)
2679                 if (stylecounts[i])
2680                         model->brushq1.light_style[model->brushq1.light_styles++] = i;
2681         j = 0;
2682         for (i = 0;i < model->brushq1.light_styles;i++)
2683         {
2684                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2685                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2686         }
2687         for (i = 0;i < model->nummodelsurfaces;i++)
2688         {
2689                 surf = model->brushq1.surfaces + model->firstmodelsurface + i;
2690                 for (j = 0;j < MAXLIGHTMAPS;j++)
2691                         if (surf->styles[j] != 255)
2692                                 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2693         }
2694         j = 0;
2695         for (i = 0;i < model->brushq1.light_styles;i++)
2696         {
2697                 *model->brushq1.light_styleupdatechains[i] = NULL;
2698                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2699                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2700         }
2701 }
2702
2703 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2704 {
2705         int i, j;
2706         for (i = 0;i < model->brushq1.numtextures;i++)
2707                 model->brushq1.pvstexturechainslength[i] = 0;
2708         for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
2709         {
2710                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2711                 {
2712                         model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2713                         model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2714                 }
2715         }
2716         for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2717         {
2718                 if (model->brushq1.pvstexturechainslength[i])
2719                 {
2720                         model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2721                         j += model->brushq1.pvstexturechainslength[i] + 1;
2722                 }
2723                 else
2724                         model->brushq1.pvstexturechains[i] = NULL;
2725         }
2726         for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
2727                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2728                         *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2729         for (i = 0;i < model->brushq1.numtextures;i++)
2730         {
2731                 if (model->brushq1.pvstexturechainslength[i])
2732                 {
2733                         *model->brushq1.pvstexturechains[i] = NULL;
2734                         model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2735                 }
2736         }
2737 }
2738
2739 //Returns PVS data for a given point
2740 //(note: can return NULL)
2741 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2742 {
2743         mnode_t *node;
2744         Mod_CheckLoaded(model);
2745         node = model->brushq1.nodes;
2746         while (node->plane)
2747                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2748         if (((mleaf_t *)node)->clusterindex >= 0)
2749                 return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2750         else
2751                 return NULL;
2752 }
2753
2754 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2755 {
2756         while (node->plane)
2757         {
2758                 float d = PlaneDiff(org, node->plane);
2759                 if (d > radius)
2760                         node = node->children[0];
2761                 else if (d < -radius)
2762                         node = node->children[1];
2763                 else
2764                 {
2765                         // go down both sides
2766                         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2767                         node = node->children[1];
2768                 }
2769         }
2770         // if this leaf is in a cluster, accumulate the pvs bits
2771         if (((mleaf_t *)node)->clusterindex >= 0)
2772         {
2773                 int i;
2774                 qbyte *pvs = model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2775                 for (i = 0;i < pvsbytes;i++)
2776                         pvsbuffer[i] |= pvs[i];
2777         }
2778 }
2779
2780 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2781 //of the given point.
2782 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2783 {
2784         int bytes = ((model->brushq1.num_leafs - 1) + 7) >> 3;
2785         bytes = min(bytes, pvsbufferlength);
2786         if (r_novis.integer || !Mod_Q1BSP_GetPVS(model, org))
2787         {
2788                 memset(pvsbuffer, 0xFF, bytes);
2789                 return bytes;
2790         }
2791         memset(pvsbuffer, 0, bytes);
2792         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2793         return bytes;
2794 }
2795
2796 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2797 {
2798         vec3_t size;
2799         const hull_t *hull;
2800
2801         VectorSubtract(inmaxs, inmins, size);
2802         if (cmodel->brush.ishlbsp)
2803         {
2804                 if (size[0] < 3)
2805                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2806                 else if (size[0] <= 32)
2807                 {
2808                         if (size[2] < 54) // pick the nearest of 36 or 72
2809                                 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2810                         else
2811                                 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2812                 }
2813                 else
2814                         hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2815         }
2816         else
2817         {
2818                 if (size[0] < 3)
2819                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2820                 else if (size[0] <= 32)
2821                         hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2822                 else
2823                         hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2824         }
2825         VectorCopy(inmins, outmins);
2826         VectorAdd(inmins, hull->clip_size, outmaxs);
2827 }
2828
2829 /*
2830 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)
2831 {
2832         mleaf_t *leaf;
2833         for (;;)
2834         {
2835                 if (!BoxesOverlap(node->mins, node->maxs, mins, maxs))
2836                         return;
2837                 if (!node->plane)
2838                         break;
2839                 Mod_Q1BSP_RecursiveGetVisible(node->children[0], model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
2840                 node = node->children[1];
2841         }
2842         leaf = (mleaf_t *)node;
2843         if ((pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
2844         {
2845                 int marksurfacenum;
2846                 msurface_t *surf;
2847                 if (maxleafs && *numleafs < maxleafs)
2848                         leaflist[(*numleafs)++] = leaf;
2849                 if (maxsurfaces)
2850                 {
2851                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
2852                         {
2853                                 surf = model->brushq1.surfaces + leaf->firstmarksurface[marksurfacenum];
2854                                 if (surf->shadowmark != shadowmarkcount)
2855                                 {
2856                                         surf->shadowmark = shadowmarkcount;
2857                                         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)
2858                                                 surfacelist[(*numsurfaces)++] = surf;
2859                                 }
2860                         }
2861                 }
2862         }
2863 }
2864
2865 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)
2866 {
2867         // FIXME: support portals
2868         if (maxsurfaces)
2869                 *numsurfaces = 0;
2870         if (maxleafs)
2871                 *numleafs = 0;
2872         pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
2873         Mod_Q1BSP_RecursiveGetVisible(ent->model->brushq1.nodes + ent->model->brushq1.firstclipnode, model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces);
2874 }
2875 */
2876
2877 extern void R_Q1BSP_DrawSky(entity_render_t *ent);
2878 extern void R_Q1BSP_Draw(entity_render_t *ent);
2879 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);
2880 extern void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
2881 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, int numsurfaces, const int *surfacelist);
2882 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2883 {
2884         int i, j, k;
2885         dheader_t *header;
2886         dmodel_t *bm;
2887         mempool_t *mainmempool;
2888         float dist, modelyawradius, modelradius, *vec;
2889         msurface_t *surf;
2890         int numshadowmeshtriangles;
2891
2892         mod->type = mod_brushq1;
2893
2894         header = (dheader_t *)buffer;
2895
2896         i = LittleLong(header->version);
2897         if (i != BSPVERSION && i != 30)
2898                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2899         mod->brush.ishlbsp = i == 30;
2900
2901         mod->soundfromcenter = true;
2902         mod->TraceBox = Mod_Q1BSP_TraceBox;
2903         mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2904         mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2905         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2906         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2907         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2908         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2909         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2910         mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2911         mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2912         mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2913         mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2914
2915         if (loadmodel->isworldmodel)
2916         {
2917                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2918                 // until we get a texture for it...
2919                 R_ResetQuakeSky();
2920         }
2921
2922 // swap all the lumps
2923         mod_base = (qbyte *)header;
2924
2925         header->version = LittleLong(header->version);
2926         for (i = 0;i < HEADER_LUMPS;i++)
2927         {
2928                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
2929                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
2930         }
2931
2932 // load into heap
2933
2934         // store which lightmap format to use
2935         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2936
2937         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2938         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2939         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2940         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2941         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2942         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2943         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2944         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2945         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2946         Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2947         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2948         // load submodels before leafs because they contain the number of vis leafs
2949         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2950         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2951         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2952         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2953
2954         if (!mod->brushq1.lightdata)
2955                 mod->brush.LightPoint = NULL;
2956
2957         if (mod->brushq1.data_compressedpvs)
2958                 Mem_Free(mod->brushq1.data_compressedpvs);
2959         mod->brushq1.data_compressedpvs = NULL;
2960         mod->brushq1.num_compressedpvs = 0;
2961
2962         Mod_Q1BSP_MakeHull0();
2963         Mod_Q1BSP_MakePortals();
2964
2965         mod->numframes = 2;             // regular and alternate animation
2966
2967         mainmempool = mod->mempool;
2968
2969         Mod_Q1BSP_LoadLightList();
2970         loadmodel = loadmodel;
2971
2972         // make a single combined shadow mesh to allow optimized shadow volume creation
2973         numshadowmeshtriangles = 0;
2974         for (j = 0, surf = loadmodel->brushq1.surfaces;j < loadmodel->brushq1.numsurfaces;j++, surf++)
2975         {
2976                 surf->num_firstshadowmeshtriangle = numshadowmeshtriangles;
2977                 numshadowmeshtriangles += surf->mesh.num_triangles;
2978         }
2979         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
2980         for (j = 0, surf = loadmodel->brushq1.surfaces;j < loadmodel->brushq1.numsurfaces;j++, surf++)
2981                 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);
2982         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
2983         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
2984         
2985         // LordHavoc: to clear the fog around the original quake submodel code, I
2986         // will explain:
2987         // first of all, some background info on the submodels:
2988         // model 0 is the map model (the world, named maps/e1m1.bsp for example)
2989         // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
2990         // now the weird for loop itself:
2991         // the loop functions in an odd way, on each iteration it sets up the
2992         // current 'mod' model (which despite the confusing code IS the model of
2993         // the number i), at the end of the loop it duplicates the model to become
2994         // the next submodel, and loops back to set up the new submodel.
2995
2996         // LordHavoc: now the explanation of my sane way (which works identically):
2997         // set up the world model, then on each submodel copy from the world model
2998         // and set up the submodel with the respective model info.
2999         for (i = 0;i < mod->brush.numsubmodels;i++)
3000         {
3001                 // LordHavoc: this code was originally at the end of this loop, but
3002                 // has been transformed to something more readable at the start here.
3003
3004                 // LordHavoc: only register submodels if it is the world
3005                 // (prevents external bsp models from replacing world submodels with
3006                 //  their own)
3007                 if (loadmodel->isworldmodel && i)
3008                 {
3009                         char name[10];
3010                         // duplicate the basic information
3011                         sprintf(name, "*%i", i);
3012                         mod = Mod_FindName(name);
3013                         // copy the base model to this one
3014                         *mod = *loadmodel;
3015                         // rename the clone back to its proper name
3016                         strcpy(mod->name, name);
3017                         // textures and memory belong to the main model
3018                         mod->texturepool = NULL;
3019                         mod->mempool = NULL;
3020                 }
3021
3022                 bm = &mod->brushq1.submodels[i];
3023
3024                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3025                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3026                 {
3027                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3028                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3029                 }
3030
3031                 mod->firstmodelsurface = bm->firstface;
3032                 mod->nummodelsurfaces = bm->numfaces;
3033
3034                 // make the model surface list (used by shadowing/lighting)
3035                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
3036                 for (j = 0;j < mod->nummodelsurfaces;j++)
3037                         mod->surfacelist[j] = mod->firstmodelsurface + j;
3038
3039                 // this gets altered below if sky is used
3040                 mod->DrawSky = NULL;
3041                 mod->Draw = R_Q1BSP_Draw;
3042                 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3043                 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3044                 mod->DrawLight = R_Q1BSP_DrawLight;
3045                 if (i != 0)
3046                 {
3047                         mod->brush.GetPVS = NULL;
3048                         mod->brush.FatPVS = NULL;
3049                         mod->brush.BoxTouchingPVS = NULL;
3050                         mod->brush.LightPoint = NULL;
3051                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3052                 }
3053                 mod->brushq1.pvstexturechains = Mem_Alloc(loadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
3054                 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(loadmodel->mempool,(mod->nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
3055                 mod->brushq1.pvstexturechainslength = Mem_Alloc(loadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
3056                 Mod_Q1BSP_BuildPVSTextureChains(mod);
3057                 Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
3058                 if (mod->nummodelsurfaces)
3059                 {
3060                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3061                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3062                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3063                         modelyawradius = 0;
3064                         modelradius = 0;
3065                         for (j = 0, surf = &mod->brushq1.surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
3066                         {
3067                                 // we only need to have a drawsky function if it is used(usually only on world model)
3068                                 if (surf->texinfo->texture->flags & SURF_DRAWSKY)
3069                                         mod->DrawSky = R_Q1BSP_DrawSky;
3070                                 // LordHavoc: submodels always clip, even if water
3071                                 if (mod->brush.numsubmodels - 1)
3072                                         surf->flags |= SURF_SOLIDCLIP;
3073                                 // calculate bounding shapes
3074                                 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
3075                                 {
3076                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3077                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3078                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3079                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3080                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3081                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3082                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3083                                         if (modelyawradius < dist)
3084                                                 modelyawradius = dist;
3085                                         dist += vec[2]*vec[2];
3086                                         if (modelradius < dist)
3087                                                 modelradius = dist;
3088                                 }
3089                         }
3090                         modelyawradius = sqrt(modelyawradius);
3091                         modelradius = sqrt(modelradius);
3092                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3093                         mod->yawmins[2] = mod->normalmins[2];
3094                         mod->yawmaxs[2] = mod->normalmaxs[2];
3095                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3096                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3097                         mod->radius = modelradius;
3098                         mod->radius2 = modelradius * modelradius;
3099                 }
3100                 else
3101                 {
3102                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3103                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3104                 }
3105                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->firstmodelsurface, mod->nummodelsurfaces, loadmodel->mempool);
3106
3107                 mod->brushq1.num_visleafs = bm->visleafs;
3108         }
3109
3110         //Mod_Q1BSP_ProcessLightList();
3111
3112         if (developer.integer)
3113                 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);
3114 }
3115
3116 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3117 {
3118 }
3119
3120 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3121 {
3122 /*
3123         d_t *in;
3124         m_t *out;
3125         int i, count;
3126
3127         in = (void *)(mod_base + l->fileofs);
3128         if (l->filelen % sizeof(*in))
3129                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3130         count = l->filelen / sizeof(*in);
3131         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3132
3133         loadmodel-> = out;
3134         loadmodel->num = count;
3135
3136         for (i = 0;i < count;i++, in++, out++)
3137         {
3138         }
3139 */
3140 }
3141
3142 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3143 {
3144 /*
3145         d_t *in;
3146         m_t *out;
3147         int i, count;
3148
3149         in = (void *)(mod_base + l->fileofs);
3150         if (l->filelen % sizeof(*in))
3151                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3152         count = l->filelen / sizeof(*in);
3153         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3154
3155         loadmodel-> = out;
3156         loadmodel->num = count;
3157
3158         for (i = 0;i < count;i++, in++, out++)
3159         {
3160         }
3161 */
3162 }
3163
3164 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3165 {
3166 /*
3167         d_t *in;
3168         m_t *out;
3169         int i, count;
3170
3171         in = (void *)(mod_base + l->fileofs);
3172         if (l->filelen % sizeof(*in))
3173                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3174         count = l->filelen / sizeof(*in);
3175         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3176
3177         loadmodel-> = out;
3178         loadmodel->num = count;
3179
3180         for (i = 0;i < count;i++, in++, out++)
3181         {
3182         }
3183 */
3184 }
3185
3186 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3187 {
3188 /*
3189         d_t *in;
3190         m_t *out;
3191         int i, count;
3192
3193         in = (void *)(mod_base + l->fileofs);
3194         if (l->filelen % sizeof(*in))
3195                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3196         count = l->filelen / sizeof(*in);
3197         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3198
3199         loadmodel-> = out;
3200         loadmodel->num = count;
3201
3202         for (i = 0;i < count;i++, in++, out++)
3203         {
3204         }
3205 */
3206 }
3207
3208 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3209 {
3210 /*
3211         d_t *in;
3212         m_t *out;
3213         int i, count;
3214
3215         in = (void *)(mod_base + l->fileofs);
3216         if (l->filelen % sizeof(*in))
3217                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3218         count = l->filelen / sizeof(*in);
3219         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3220
3221         loadmodel-> = out;
3222         loadmodel->num = count;
3223
3224         for (i = 0;i < count;i++, in++, out++)
3225         {
3226         }
3227 */
3228 }
3229
3230 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3231 {
3232 /*
3233         d_t *in;
3234         m_t *out;
3235         int i, count;
3236
3237         in = (void *)(mod_base + l->fileofs);
3238         if (l->filelen % sizeof(*in))
3239                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3240         count = l->filelen / sizeof(*in);
3241         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3242
3243         loadmodel-> = out;
3244         loadmodel->num = count;
3245
3246         for (i = 0;i < count;i++, in++, out++)
3247         {
3248         }
3249 */
3250 }
3251
3252 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3253 {
3254 /*
3255         d_t *in;
3256         m_t *out;
3257         int i, count;
3258
3259         in = (void *)(mod_base + l->fileofs);
3260         if (l->filelen % sizeof(*in))
3261                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3262         count = l->filelen / sizeof(*in);
3263         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3264
3265         loadmodel-> = out;
3266         loadmodel->num = count;
3267
3268         for (i = 0;i < count;i++, in++, out++)
3269         {
3270         }
3271 */
3272 }
3273
3274 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3275 {
3276 /*
3277         d_t *in;
3278         m_t *out;
3279         int i, count;
3280
3281         in = (void *)(mod_base + l->fileofs);
3282         if (l->filelen % sizeof(*in))
3283                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3284         count = l->filelen / sizeof(*in);
3285         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3286
3287         loadmodel-> = out;
3288         loadmodel->num = count;
3289
3290         for (i = 0;i < count;i++, in++, out++)
3291         {
3292         }
3293 */
3294 }
3295
3296 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3297 {
3298 /*
3299         d_t *in;
3300         m_t *out;
3301         int i, count;
3302
3303         in = (void *)(mod_base + l->fileofs);
3304         if (l->filelen % sizeof(*in))
3305                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3306         count = l->filelen / sizeof(*in);
3307         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3308
3309         loadmodel-> = out;
3310         loadmodel->num = count;
3311
3312         for (i = 0;i < count;i++, in++, out++)
3313         {
3314         }
3315 */
3316 }
3317
3318 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3319 {
3320 /*
3321         d_t *in;
3322         m_t *out;
3323         int i, count;
3324
3325         in = (void *)(mod_base + l->fileofs);
3326         if (l->filelen % sizeof(*in))
3327                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3328         count = l->filelen / sizeof(*in);
3329         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3330
3331         loadmodel-> = out;
3332         loadmodel->num = count;
3333
3334         for (i = 0;i < count;i++, in++, out++)
3335         {
3336         }
3337 */
3338 }
3339
3340 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3341 {
3342 /*
3343         d_t *in;
3344         m_t *out;
3345         int i, count;
3346
3347         in = (void *)(mod_base + l->fileofs);
3348         if (l->filelen % sizeof(*in))
3349                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3350         count = l->filelen / sizeof(*in);
3351         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3352
3353         loadmodel-> = out;
3354         loadmodel->num = count;
3355
3356         for (i = 0;i < count;i++, in++, out++)
3357         {
3358         }
3359 */
3360 }
3361
3362 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3363 {
3364 /*
3365         d_t *in;
3366         m_t *out;
3367         int i, count;
3368
3369         in = (void *)(mod_base + l->fileofs);
3370         if (l->filelen % sizeof(*in))
3371                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3372         count = l->filelen / sizeof(*in);
3373         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3374
3375         loadmodel-> = out;
3376         loadmodel->num = count;
3377
3378         for (i = 0;i < count;i++, in++, out++)
3379         {
3380         }
3381 */
3382 }
3383
3384 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3385 {
3386 /*
3387         d_t *in;
3388         m_t *out;
3389         int i, count;
3390
3391         in = (void *)(mod_base + l->fileofs);
3392         if (l->filelen % sizeof(*in))
3393                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3394         count = l->filelen / sizeof(*in);
3395         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3396
3397         loadmodel-> = out;
3398         loadmodel->num = count;
3399
3400         for (i = 0;i < count;i++, in++, out++)
3401         {
3402         }
3403 */
3404 }
3405
3406 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3407 {
3408 /*
3409         d_t *in;
3410         m_t *out;
3411         int i, count;
3412
3413         in = (void *)(mod_base + l->fileofs);
3414         if (l->filelen % sizeof(*in))
3415                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3416         count = l->filelen / sizeof(*in);
3417         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3418
3419         loadmodel-> = out;
3420         loadmodel->num = count;
3421
3422         for (i = 0;i < count;i++, in++, out++)
3423         {
3424         }
3425 */
3426 }
3427
3428 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3429 {
3430 /*
3431         d_t *in;
3432         m_t *out;
3433         int i, count;
3434
3435         in = (void *)(mod_base + l->fileofs);
3436         if (l->filelen % sizeof(*in))
3437                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3438         count = l->filelen / sizeof(*in);
3439         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3440
3441         loadmodel-> = out;
3442         loadmodel->num = count;
3443
3444         for (i = 0;i < count;i++, in++, out++)
3445         {
3446         }
3447 */
3448 }
3449
3450 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3451 {
3452 /*
3453         d_t *in;
3454         m_t *out;
3455         int i, count;
3456
3457         in = (void *)(mod_base + l->fileofs);
3458         if (l->filelen % sizeof(*in))
3459                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3460         count = l->filelen / sizeof(*in);
3461         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3462
3463         loadmodel-> = out;
3464         loadmodel->num = count;
3465
3466         for (i = 0;i < count;i++, in++, out++)
3467         {
3468         }
3469 */
3470 }
3471
3472 static void Mod_Q2BSP_LoadModels(lump_t *l)
3473 {
3474 /*
3475         d_t *in;
3476         m_t *out;
3477         int i, count;
3478
3479         in = (void *)(mod_base + l->fileofs);
3480         if (l->filelen % sizeof(*in))
3481                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3482         count = l->filelen / sizeof(*in);
3483         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3484
3485         loadmodel-> = out;
3486         loadmodel->num = count;
3487
3488         for (i = 0;i < count;i++, in++, out++)
3489         {
3490         }
3491 */
3492 }
3493
3494 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3495 {
3496         int i;
3497         q2dheader_t *header;
3498
3499         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3500
3501         mod->type = mod_brushq2;
3502
3503         header = (q2dheader_t *)buffer;
3504
3505         i = LittleLong(header->version);
3506         if (i != Q2BSPVERSION)
3507                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3508         mod->brush.ishlbsp = false;
3509         if (loadmodel->isworldmodel)
3510         {
3511                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3512                 // until we get a texture for it...
3513                 R_ResetQuakeSky();
3514         }
3515
3516         mod_base = (qbyte *)header;
3517
3518         // swap all the lumps
3519         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3520                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3521
3522         // store which lightmap format to use
3523         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3524
3525         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3526         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3527         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3528         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3529         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3530         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3531         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3532         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3533         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3534         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3535         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3536         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3537         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3538         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3539         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3540         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3541         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3542         // LordHavoc: must go last because this makes the submodels
3543         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3544 }
3545
3546 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3547 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3548
3549 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3550 {
3551         const char *data;
3552         char key[128], value[4096];
3553         float v[3];
3554         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3555         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3556         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3557         if (!l->filelen)
3558                 return;
3559         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3560         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3561         data = loadmodel->brush.entities;
3562         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3563         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3564         {
3565                 while (1)
3566                 {
3567                         if (!COM_ParseToken(&data, false))
3568                                 break; // error
3569                         if (com_token[0] == '}')
3570                                 break; // end of worldspawn
3571                         if (com_token[0] == '_')
3572                                 strcpy(key, com_token + 1);
3573                         else
3574                                 strcpy(key, com_token);
3575                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3576                                 key[strlen(key)-1] = 0;
3577                         if (!COM_ParseToken(&data, false))
3578                                 break; // error
3579                         strcpy(value, com_token);
3580                         if (!strcmp("gridsize", key))
3581                         {
3582                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3583                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3584                         }
3585                 }
3586         }
3587 }
3588
3589 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3590 {
3591         q3dtexture_t *in;
3592         q3mtexture_t *out;
3593         int i, count;
3594         int j, c;
3595         fssearch_t *search;
3596         char *f;
3597         const char *text;
3598         int flags;
3599         char shadername[Q3PATHLENGTH];
3600         char sky[Q3PATHLENGTH];
3601
3602         in = (void *)(mod_base + l->fileofs);
3603         if (l->filelen % sizeof(*in))
3604                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3605         count = l->filelen / sizeof(*in);
3606         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3607
3608         loadmodel->brushq3.data_textures = out;
3609         loadmodel->brushq3.num_textures = count;
3610
3611         for (i = 0;i < count;i++, in++, out++)
3612         {
3613                 out->number = i;
3614                 strlcpy (out->name, in->name, sizeof (out->name));
3615                 out->surfaceflags = LittleLong(in->surfaceflags);
3616                 out->nativecontents = LittleLong(in->contents);
3617                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3618                 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true, true);
3619                 out->surfaceparms = -1;
3620         }
3621
3622         // do a quick parse of shader files to get surfaceparms
3623         if ((search = FS_Search("scripts/*.shader", true, false)))
3624         {
3625                 for (i = 0;i < search->numfilenames;i++)
3626                 {
3627                         if ((f = FS_LoadFile(search->filenames[i], tempmempool, false)))
3628                         {
3629                                 text = f;
3630                                 while (COM_ParseToken(&text, false))
3631                                 {
3632                                         strlcpy (shadername, com_token, sizeof (shadername));
3633                                         flags = 0;
3634                                         sky[0] = 0;
3635                                         if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
3636                                         {
3637                                                 while (COM_ParseToken(&text, false))
3638                                                 {
3639                                                         if (!strcasecmp(com_token, "}"))
3640                                                                 break;
3641                                                         else if (!strcasecmp(com_token, "{"))
3642                                                         {
3643                                                                 while (COM_ParseToken(&text, false))
3644                                                                 {
3645                                                                         if (!strcasecmp(com_token, "}"))
3646                                                                                 break;
3647                                                                 }
3648                                                         }
3649                                                         else if (!strcasecmp(com_token, "surfaceparm"))
3650                                                         {
3651                                                                 if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3652                                                                 {
3653                                                                         if (!strcasecmp(com_token, "alphashadow"))
3654                                                                                 flags |= Q3SURFACEPARM_ALPHASHADOW;
3655                                                                         else if (!strcasecmp(com_token, "areaportal"))
3656                                                                                 flags |= Q3SURFACEPARM_AREAPORTAL;
3657                                                                         else if (!strcasecmp(com_token, "clusterportal"))
3658                                                                                 flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3659                                                                         else if (!strcasecmp(com_token, "detail"))
3660                                                                                 flags |= Q3SURFACEPARM_DETAIL;
3661                                                                         else if (!strcasecmp(com_token, "donotenter"))
3662                                                                                 flags |= Q3SURFACEPARM_DONOTENTER;
3663                                                                         else if (!strcasecmp(com_token, "fog"))
3664                                                                                 flags |= Q3SURFACEPARM_FOG;
3665                                                                         else if (!strcasecmp(com_token, "lava"))
3666                                                                                 flags |= Q3SURFACEPARM_LAVA;
3667                                                                         else if (!strcasecmp(com_token, "lightfilter"))
3668                                                                                 flags |= Q3SURFACEPARM_LIGHTFILTER;
3669                                                                         else if (!strcasecmp(com_token, "metalsteps"))
3670                                                                                 flags |= Q3SURFACEPARM_METALSTEPS;
3671                                                                         else if (!strcasecmp(com_token, "nodamage"))
3672                                                                                 flags |= Q3SURFACEPARM_NODAMAGE;
3673                                                                         else if (!strcasecmp(com_token, "nodlight"))
3674                                                                                 flags |= Q3SURFACEPARM_NODLIGHT;
3675                                                                         else if (!strcasecmp(com_token, "nodraw"))
3676                                                                                 flags |= Q3SURFACEPARM_NODRAW;
3677                                                                         else if (!strcasecmp(com_token, "nodrop"))
3678                                                                                 flags |= Q3SURFACEPARM_NODROP;
3679                                                                         else if (!strcasecmp(com_token, "noimpact"))
3680                                                                                 flags |= Q3SURFACEPARM_NOIMPACT;
3681                                                                         else if (!strcasecmp(com_token, "nolightmap"))
3682                                                                                 flags |= Q3SURFACEPARM_NOLIGHTMAP;
3683                                                                         else if (!strcasecmp(com_token, "nomarks"))
3684                                                                                 flags |= Q3SURFACEPARM_NOMARKS;
3685                                                                         else if (!strcasecmp(com_token, "nomipmaps"))
3686                                                                                 flags |= Q3SURFACEPARM_NOMIPMAPS;
3687                                                                         else if (!strcasecmp(com_token, "nonsolid"))
3688                                                                                 flags |= Q3SURFACEPARM_NONSOLID;
3689                                                                         else if (!strcasecmp(com_token, "origin"))
3690                                                                                 flags |= Q3SURFACEPARM_ORIGIN;
3691                                                                         else if (!strcasecmp(com_token, "playerclip"))
3692                                                                                 flags |= Q3SURFACEPARM_PLAYERCLIP;
3693                                                                         else if (!strcasecmp(com_token, "sky"))
3694                                                                                 flags |= Q3SURFACEPARM_SKY;
3695                                                                         else if (!strcasecmp(com_token, "slick"))
3696                                                                                 flags |= Q3SURFACEPARM_SLICK;
3697                                                                         else if (!strcasecmp(com_token, "slime"))
3698                                                                                 flags |= Q3SURFACEPARM_SLIME;
3699                                                                         else if (!strcasecmp(com_token, "structural"))
3700                                                                                 flags |= Q3SURFACEPARM_STRUCTURAL;
3701                                                                         else if (!strcasecmp(com_token, "trans"))
3702                                                                                 flags |= Q3SURFACEPARM_TRANS;
3703                                                                         else if (!strcasecmp(com_token, "water"))
3704                                                                                 flags |= Q3SURFACEPARM_WATER;
3705                                                                         else
3706                                                                                 Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
3707                                                                         if (!COM_ParseToken(&text, true) || strcasecmp(com_token, "\n"))
3708                                                                         {
3709                                                                                 Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
3710                                                                                 goto parseerror;
3711                                                                         }
3712                                                                 }
3713                                                                 else
3714                                                                 {
3715                                                                         Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
3716                                                                         goto parseerror;
3717                                                                 }
3718                                                         }
3719                                                         else if (!strcasecmp(com_token, "sky"))
3720                                                         {
3721                                                                 if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3722                                                                         if (strlen(com_token) < sizeof(sky))
3723                                                                                 strcpy(sky, com_token);
3724                                                         }
3725                                                         else if (!strcasecmp(com_token, "skyparms"))
3726                                                         {
3727                                                                 if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3728                                                                 {
3729                                                                         if (strlen(com_token) < sizeof(sky) && !atoi(com_token) && strcasecmp(com_token, "-"))
3730                                                                                 strcpy(sky, com_token);
3731                                                                         if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3732                                                                                 COM_ParseToken(&text, true);
3733                                                                 }
3734                                                         }
3735                                                         else
3736                                                         {
3737                                                                 // look for linebreak or }
3738                                                                 while(COM_ParseToken(&text, true) && strcasecmp(com_token, "\n") && strcasecmp(com_token, "}"));
3739                                                                 // break out to top level if it was }
3740                                                                 if (!strcasecmp(com_token, "}"))
3741                                                                         break;
3742                                                         }
3743                                                 }
3744                                                 // add shader to list (shadername and flags)
3745                                                 // actually here we just poke into the texture settings
3746                                                 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3747                                                 {
3748                                                         if (!strcasecmp(out->name, shadername))
3749                                                         {
3750                                                                 out->surfaceparms = flags;
3751                                                                 if ((flags & Q3SURFACEPARM_SKY) && sky[0])
3752                                                                 {
3753                                                                         // quake3 seems to append a _ to the skybox name, so this must do so as well
3754                                                                         snprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
3755                                                                 }
3756                                                         }
3757                                                 }
3758                                         }
3759                                         else
3760                                         {
3761                                                 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3762                                                 goto parseerror;
3763                                         }
3764                                 }
3765 parseerror:
3766                                 Mem_Free(f);
3767                         }
3768                 }
3769         }
3770
3771         c = 0;
3772         for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3773         {
3774                 if (out->surfaceparms == -1)
3775                 {
3776                         c++;
3777                         Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3778                         out->surfaceparms = 0;
3779                         // these are defaults
3780                         if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3781                          || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3782                                 out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3783                         if (!strncmp(out->name, "textures/skies/", 15))
3784                                 out->surfaceparms |= Q3SURFACEPARM_SKY;
3785                         if (R_TextureHasAlpha(out->skin.base))
3786                                 out->surfaceparms |= Q3SURFACEPARM_TRANS;
3787                 }
3788         }
3789         Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3790 }
3791
3792 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3793 {
3794         q3dplane_t *in;
3795         mplane_t *out;
3796         int i, count;
3797
3798         in = (void *)(mod_base + l->fileofs);
3799         if (l->filelen % sizeof(*in))
3800                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3801         count = l->filelen / sizeof(*in);
3802         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3803
3804         loadmodel->brushq3.data_planes = out;
3805         loadmodel->brushq3.num_planes = count;
3806
3807         for (i = 0;i < count;i++, in++, out++)
3808         {
3809                 out->normal[0] = LittleLong(in->normal[0]);
3810                 out->normal[1] = LittleLong(in->normal[1]);
3811                 out->normal[2] = LittleLong(in->normal[2]);
3812                 out->dist = LittleLong(in->dist);
3813                 PlaneClassify(out);
3814         }
3815 }
3816
3817 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3818 {
3819         q3dbrushside_t *in;
3820         q3mbrushside_t *out;
3821         int i, n, count;
3822
3823         in = (void *)(mod_base + l->fileofs);
3824         if (l->filelen % sizeof(*in))
3825                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3826         count = l->filelen / sizeof(*in);
3827         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3828
3829         loadmodel->brushq3.data_brushsides = out;
3830         loadmodel->brushq3.num_brushsides = count;
3831
3832         for (i = 0;i < count;i++, in++, out++)
3833         {
3834                 n = LittleLong(in->planeindex);
3835                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3836                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3837                 out->plane = loadmodel->brushq3.data_planes + n;
3838                 n = LittleLong(in->textureindex);
3839                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3840                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3841                 out->texture = loadmodel->brushq3.data_textures + n;
3842         }
3843 }
3844
3845 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3846 {
3847         q3dbrush_t *in;
3848         q3mbrush_t *out;
3849         int i, j, n, c, count, maxplanes;
3850         mplane_t *planes;
3851
3852         in = (void *)(mod_base + l->fileofs);
3853         if (l->filelen % sizeof(*in))
3854                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3855         count = l->filelen / sizeof(*in);
3856         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3857
3858         loadmodel->brushq3.data_brushes = out;
3859         loadmodel->brushq3.num_brushes = count;
3860
3861         maxplanes = 0;
3862         planes = NULL;
3863
3864         for (i = 0;i < count;i++, in++, out++)
3865         {
3866                 n = LittleLong(in->firstbrushside);
3867                 c = LittleLong(in->numbrushsides);
3868                 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3869                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3870                 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3871                 out->numbrushsides = c;
3872                 n = LittleLong(in->textureindex);
3873                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3874                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3875                 out->texture = loadmodel->brushq3.data_textures + n;
3876
3877                 // make a list of mplane_t structs to construct a colbrush from
3878                 if (maxplanes < out->numbrushsides)
3879                 {
3880                         maxplanes = out->numbrushsides;
3881                         if (planes)
3882                                 Mem_Free(planes);
3883                         planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3884                 }
3885                 for (j = 0;j < out->numbrushsides;j++)
3886                 {
3887                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3888                         planes[j].dist = out->firstbrushside[j].plane->dist;
3889                 }
3890                 // make the colbrush from the planes
3891                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3892         }
3893         if (planes)
3894                 Mem_Free(planes);
3895 }
3896
3897 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3898 {
3899         q3deffect_t *in;
3900         q3meffect_t *out;
3901         int i, n, count;
3902
3903         in = (void *)(mod_base + l->fileofs);
3904         if (l->filelen % sizeof(*in))
3905                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3906         count = l->filelen / sizeof(*in);
3907         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3908
3909         loadmodel->brushq3.data_effects = out;
3910         loadmodel->brushq3.num_effects = count;
3911
3912         for (i = 0;i < count;i++, in++, out++)
3913         {
3914                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3915                 n = LittleLong(in->brushindex);
3916                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3917                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3918                 out->brush = loadmodel->brushq3.data_brushes + n;
3919                 out->unknown = LittleLong(in->unknown);
3920         }
3921 }
3922
3923 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3924 {
3925         q3dvertex_t *in;
3926         int i, count;
3927
3928         in = (void *)(mod_base + l->fileofs);
3929         if (l->filelen % sizeof(*in))
3930                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3931         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3932         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3933         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3934         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3935         loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3936         loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3937         loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3938         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3939
3940         for (i = 0;i < count;i++, in++)
3941         {
3942                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3943                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3944                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3945                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3946                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3947                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3948                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3949                 // svector/tvector are calculated later in face loading
3950                 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3951                 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3952                 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3953                 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3954                 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3955                 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3956                 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3957                 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3958                 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3959                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3960                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3961                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3962                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3963         }
3964 }
3965
3966 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3967 {
3968         int *in;
3969         int *out;
3970         int i, count;
3971
3972         in = (void *)(mod_base + l->fileofs);
3973         if (l->filelen % sizeof(int[3]))
3974                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3975         count = l->filelen / sizeof(*in);
3976         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3977
3978         loadmodel->brushq3.num_triangles = count / 3;
3979         loadmodel->brushq3.data_element3i = out;
3980         loadmodel->brushq3.data_neighbor3i = out + count;
3981
3982         for (i = 0;i < count;i++, in++, out++)
3983         {
3984                 *out = LittleLong(*in);
3985                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3986                 {
3987                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3988                         *out = 0;
3989                 }
3990         }
3991 }
3992
3993 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3994 {
3995         q3dlightmap_t *in;
3996         rtexture_t **out;
3997         int i, count;
3998
3999         if (!l->filelen)
4000                 return;
4001         in = (void *)(mod_base + l->fileofs);
4002         if (l->filelen % sizeof(*in))
4003                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4004         count = l->filelen / sizeof(*in);
4005         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4006
4007         loadmodel->brushq3.data_lightmaps = out;
4008         loadmodel->brushq3.num_lightmaps = count;
4009
4010         for (i = 0;i < count;i++, in++, out++)
4011                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4012 }
4013
4014 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4015 {
4016         q3dface_t *in;
4017         q3msurface_t *out;
4018         int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
4019         //int *originalelement3i;
4020         //int *originalneighbor3i;
4021         float *originalvertex3f;
4022         //float *originalsvector3f;
4023         //float *originaltvector3f;
4024         //float *originalnormal3f;
4025         float *originalcolor4f;
4026         float *originaltexcoordtexture2f;
4027         float *originaltexcoordlightmap2f;
4028         float *v;
4029
4030         in = (void *)(mod_base + l->fileofs);
4031         if (l->filelen % sizeof(*in))
4032                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4033         count = l->filelen / sizeof(*in);
4034         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4035
4036         loadmodel->brushq3.data_faces = out;
4037         loadmodel->brushq3.num_faces = count;
4038
4039         for (i = 0;i < count;i++, in++, out++)
4040         {
4041                 // check face type first
4042                 out->type = LittleLong(in->type);
4043                 if (out->type != Q3FACETYPE_POLYGON
4044                  && out->type != Q3FACETYPE_PATCH
4045                  && out->type != Q3FACETYPE_MESH
4046                  && out->type != Q3FACETYPE_FLARE)
4047                 {
4048                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
4049                         out->num_vertices = 0;
4050                         out->num_triangles = 0;
4051                         out->type = 0; // error
4052                         continue;
4053                 }
4054
4055                 n = LittleLong(in->textureindex);
4056                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
4057                 {
4058                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
4059                         out->num_vertices = 0;
4060                         out->num_triangles = 0;
4061                         out->type = 0; // error
4062                         continue;
4063                         n = 0;
4064                 }
4065                 out->texture = loadmodel->brushq3.data_textures + n;
4066                 n = LittleLong(in->effectindex);
4067                 if (n < -1 || n >= loadmodel->brushq3.num_effects)
4068                 {
4069                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4070                         n = -1;
4071                 }
4072                 if (n == -1)
4073                         out->effect = NULL;
4074                 else
4075                         out->effect = loadmodel->brushq3.data_effects + n;
4076                 n = LittleLong(in->lightmapindex);
4077                 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
4078                 {
4079                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4080                         n = -1;
4081                 }
4082                 if (n == -1)
4083                         out->lightmaptexture = NULL;
4084                 else
4085                         out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4086
4087                 out->firstvertex = LittleLong(in->firstvertex);
4088                 out->num_vertices = LittleLong(in->numvertices);
4089                 out->firstelement = LittleLong(in->firstelement);
4090                 out->num_triangles = LittleLong(in->numelements) / 3;
4091                 if (out->num_triangles * 3 != LittleLong(in->numelements))
4092                 {
4093                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, LittleLong(in->numelements));
4094                         out->num_vertices = 0;
4095                         out->num_triangles = 0;
4096                         out->type = 0; // error
4097                         continue;
4098                 }
4099                 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
4100                 {
4101                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->num_vertices, loadmodel->brushq3.num_vertices);
4102                         out->num_vertices = 0;
4103                         out->num_triangles = 0;
4104                         out->type = 0; // error
4105                         continue;
4106                 }
4107                 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
4108                 {
4109                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->num_triangles * 3, loadmodel->brushq3.num_triangles * 3);
4110                         out->num_vertices = 0;
4111                         out->num_triangles = 0;
4112                         out->type = 0; // error
4113                         continue;
4114                 }
4115                 switch(out->type)
4116                 {
4117                 case Q3FACETYPE_POLYGON:
4118                 case Q3FACETYPE_MESH:
4119                         // no processing necessary
4120                         out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4121                         out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4122                         out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4123                         out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4124                         out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4125                         out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4126                         out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4127                         out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4128                         out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4129                         break;
4130                 case Q3FACETYPE_PATCH:
4131                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4132                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4133                         if (patchsize[0] < 1 || patchsize[1] < 1)
4134                         {
4135                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4136                                 out->num_vertices = 0;
4137                                 out->num_triangles = 0;
4138                                 out->type = 0; // error
4139                                 continue;
4140                         }
4141                         originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4142                         //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4143                         //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4144                         //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4145                         originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4146                         originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4147                         originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4148                         //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
4149                         //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4150                         /*
4151                         originalvertex3f = out->data_vertex3f;
4152                         //originalsvector3f = out->data_svector3f;
4153                         //originaltvector3f = out->data_tvector3f;
4154                         //originalnormal3f = out->data_normal3f;
4155                         originalcolor4f = out->data_color4f;
4156                         originaltexcoordtexture2f = out->data_texcoordtexture2f;
4157                         originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
4158                         //originalelement3i = out->data_element3i;
4159                         //originalneighbor3i = out->data_neighbor3i;
4160                         */
4161                         // convert patch to Q3FACETYPE_MESH
4162                         xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
4163                         ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
4164                         // bound to user settings
4165                         xlevel = bound(r_subdivisions_minlevel.integer, xlevel, r_subdivisions_maxlevel.integer);
4166                         ylevel = bound(r_subdivisions_minlevel.integer, ylevel, r_subdivisions_maxlevel.integer);
4167                         // bound to sanity settings
4168                         xlevel = bound(0, xlevel, 10);
4169                         ylevel = bound(0, ylevel, 10);
4170                         // bound to user limit on vertices
4171                         while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4172                         {
4173                                 if (xlevel > ylevel)
4174                                         xlevel--;
4175                                 else
4176                                         ylevel--;
4177                         }
4178                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
4179                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
4180                         finalvertices = finalwidth * finalheight;
4181                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4182                         out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
4183                         out->data_svector3f = out->data_vertex3f + finalvertices * 3;
4184                         out->data_tvector3f = out->data_svector3f + finalvertices * 3;
4185                         out->data_normal3f = out->data_tvector3f + finalvertices * 3;
4186                         out->data_color4f = out->data_normal3f + finalvertices * 3;
4187                         out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
4188                         out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
4189                         out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
4190                         out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
4191                         out->type = Q3FACETYPE_MESH;
4192                         out->firstvertex = -1;
4193                         out->num_vertices = finalvertices;
4194                         out->firstelement = -1;
4195                         out->num_triangles = finaltriangles;
4196                         // generate geometry
4197                         // (note: normals are skipped because they get recalculated)
4198                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
4199                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
4200                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
4201                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
4202                         // generate elements
4203                         e = out->data_element3i;
4204                         for (y = 0;y < finalheight - 1;y++)
4205                         {
4206                                 row0 = (y + 0) * finalwidth;
4207                                 row1 = (y + 1) * finalwidth;
4208                                 for (x = 0;x < finalwidth - 1;x++)
4209                                 {
4210                                         *e++ = row0;
4211                                         *e++ = row1;
4212                                         *e++ = row0 + 1;
4213                                         *e++ = row1;
4214                                         *e++ = row1 + 1;
4215                                         *e++ = row0 + 1;
4216                                         row0++;
4217                                         row1++;
4218                                 }
4219                         }
4220                         out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
4221                         if (developer.integer)
4222                         {
4223                                 if (out->num_triangles < finaltriangles)
4224                                         Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_vertices, finaltriangles, finaltriangles - out->num_triangles, out->num_triangles);
4225                                 else
4226                                         Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_vertices, out->num_triangles);
4227                         }
4228                         // q3map does not put in collision brushes for curves... ugh
4229                         // build the lower quality collision geometry
4230                         out->collisions = true;
4231                         xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
4232                         ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
4233                         // bound to user settings
4234                         xlevel = bound(r_subdivisions_collision_minlevel.integer, xlevel, r_subdivisions_collision_maxlevel.integer);
4235                         ylevel = bound(r_subdivisions_collision_minlevel.integer, ylevel, r_subdivisions_collision_maxlevel.integer);
4236                         // bound to sanity settings
4237                         xlevel = bound(0, xlevel, 10);
4238                         ylevel = bound(0, ylevel, 10);
4239                         // bound to user limit on vertices
4240                         while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
4241                         {
4242                                 if (xlevel > ylevel)
4243                                         xlevel--;
4244                                 else
4245                                         ylevel--;
4246                         }
4247                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
4248                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
4249                         finalvertices = finalwidth * finalheight;
4250                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4251                         out->data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
4252                         out->data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
4253                         out->num_collisionvertices = finalvertices;
4254                         out->num_collisiontriangles = finaltriangles;
4255                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_collisionvertex3f);
4256                         // generate elements
4257                         e = out->data_collisionelement3i;
4258                         for (y = 0;y < finalheight - 1;y++)
4259                         {
4260                                 row0 = (y + 0) * finalwidth;
4261                                 row1 = (y + 1) * finalwidth;
4262                                 for (x = 0;x < finalwidth - 1;x++)
4263                                 {
4264                                         *e++ = row0;
4265                                         *e++ = row1;
4266                                         *e++ = row0 + 1;
4267                                         *e++ = row1;
4268                                         *e++ = row1 + 1;
4269                                         *e++ = row0 + 1;
4270                                         row0++;
4271                                         row1++;
4272                                 }
4273                         }
4274                         out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
4275                         if (developer.integer)
4276                         {
4277                                 if (out->num_collisiontriangles < finaltriangles)
4278                                         Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles, %i degenerate triangles removed (leaving %i)\n", patchsize[0], patchsize[1], out->num_collisionvertices, finaltriangles, finaltriangles - out->num_collisiontriangles, out->num_collisiontriangles);
4279                                 else
4280                                         Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided for collisions to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->num_collisionvertices, out->num_collisiontriangles);
4281                         }
4282                         break;
4283                 case Q3FACETYPE_FLARE:
4284                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4285                         // don't render it
4286                         out->num_vertices = 0;
4287                         out->num_triangles = 0;
4288                         out->type = 0;
4289                         break;
4290                 }
4291                 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4292                         if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4293                                 invalidelements++;
4294                 if (invalidelements)
4295                 {
4296                         Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->num_vertices, out->firstelement, out->num_triangles * 3);
4297                         for (j = 0;j < out->num_triangles * 3;j++)
4298                         {
4299                                 Con_Printf(" %i", out->data_element3i[j]);
4300                                 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4301                                         out->data_element3i[j] = 0;
4302                         }
4303                         Con_Print("\n");
4304                 }
4305                 // for shadow volumes
4306                 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
4307                 // for per pixel lighting
4308                 Mod_BuildTextureVectorsAndNormals(out->num_vertices, out->num_triangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
4309                 // calculate a bounding box
4310                 VectorClear(out->mins);
4311                 VectorClear(out->maxs);
4312                 if (out->num_vertices)
4313                 {
4314                         VectorCopy(out->data_vertex3f, out->mins);
4315                         VectorCopy(out->data_vertex3f, out->maxs);
4316                         for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
4317                         {
4318                                 out->mins[0] = min(out->mins[0], v[0]);
4319                                 out->maxs[0] = max(out->maxs[0], v[0]);
4320                                 out->mins[1] = min(out->mins[1], v[1]);
4321                                 out->maxs[1] = max(out->maxs[1], v[1]);
4322                                 out->mins[2] = min(out->mins[2], v[2]);
4323                                 out->maxs[2] = max(out->maxs[2], v[2]);
4324                         }
4325                         out->mins[0] -= 1.0f;
4326                         out->mins[1] -= 1.0f;
4327                         out->mins[2] -= 1.0f;
4328                         out->maxs[0] += 1.0f;
4329                         out->maxs[1] += 1.0f;
4330                         out->maxs[2] += 1.0f;
4331                 }
4332         }
4333
4334         // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
4335         /*
4336         {
4337         int totalverts, totaltris;
4338         int originalnum_vertices;
4339         float *originaldata_vertex3f;
4340         float *originaldata_texcoordtexture2f;
4341         float *originaldata_texcoordlightmap2f;
4342         float *originaldata_svector3f;
4343         float *originaldata_tvector3f;
4344         float *originaldata_normal3f;
4345         float *originaldata_color4f;
4346         int originalnum_triangles;
4347         int *originaldata_element3i;
4348         int *originaldata_neighbor3i;
4349
4350         totalverts = 0;
4351         totaltris = 0;
4352         for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4353         {
4354                 if (!out->type)
4355                         continue;
4356                 totalverts += out->num_vertices;
4357                 totaltris += out->num_triangles;
4358         }
4359
4360         originalnum_vertices = loadmodel->brushq3.num_vertices;
4361         originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
4362         originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
4363         originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
4364         originaldata_svector3f = loadmodel->brushq3.data_svector3f;
4365         originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
4366         originaldata_normal3f = loadmodel->brushq3.data_normal3f;
4367         originaldata_color4f = loadmodel->brushq3.data_color4f;
4368         originalnum_triangles = loadmodel->brushq3.num_triangles;
4369         originaldata_element3i = loadmodel->brushq3.data_element3i;
4370         originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
4371         loadmodel->brushq3.num_vertices = totalverts;
4372         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
4373         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
4374         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
4375         loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
4376         loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
4377         loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
4378         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
4379         loadmodel->brushq3.num_triangles = totaltris;
4380         loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
4381         loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
4382         totalverts = 0;
4383         totaltris = 0;
4384         for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4385         {
4386                 if (!out->type)
4387                         continue;
4388                 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
4389                 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
4390                 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
4391                 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
4392                 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
4393                 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
4394                 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
4395                 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
4396                 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
4397                 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
4398                 if (out->firstvertex == -1)
4399                         Mem_Free(out->data_vertex3f);
4400                 if (out->firstelement == -1)
4401                         Mem_Free(out->data_element3i);
4402                 out->firstvertex = totalverts;
4403                 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4404                 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4405                 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4406                 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4407                 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4408                 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4409                 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4410                 out->firstelement = totaltris * 3;
4411                 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4412                 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4413                 //for (j = 0;j < out->numtriangles * 3;j++)
4414                 //      out->data_element3i[j] += totalverts - out->firstvertex;
4415                 totalverts += out->num_vertices;
4416                 totaltris += out->num_triangles;
4417         }
4418         Mem_Free(originaldata_vertex3f);
4419         Mem_Free(originaldata_element3i);
4420         }
4421         */
4422 }
4423
4424 static void Mod_Q3BSP_LoadModels(lump_t *l)
4425 {
4426         q3dmodel_t *in;
4427         q3mmodel_t *out;
4428         int i, j, n, c, count;
4429
4430         in = (void *)(mod_base + l->fileofs);
4431         if (l->filelen % sizeof(*in))
4432                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4433         count = l->filelen / sizeof(*in);
4434         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4435
4436         loadmodel->brushq3.data_models = out;
4437         loadmodel->brushq3.num_models = count;
4438
4439         for (i = 0;i < count;i++, in++, out++)
4440         {
4441                 for (j = 0;j < 3;j++)
4442                 {
4443                         out->mins[j] = LittleFloat(in->mins[j]);
4444                         out->maxs[j] = LittleFloat(in->maxs[j]);
4445                 }
4446                 n = LittleLong(in->firstface);
4447                 c = LittleLong(in->numfaces);
4448                 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4449                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4450                 out->firstface = loadmodel->brushq3.data_faces + n;
4451                 out->numfaces = c;
4452                 n = LittleLong(in->firstbrush);
4453                 c = LittleLong(in->numbrushes);
4454                 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4455                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4456                 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4457                 out->numbrushes = c;
4458         }
4459 }
4460
4461 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4462 {
4463         int *in;
4464         q3mbrush_t **out;
4465         int i, n, count;
4466
4467         in = (void *)(mod_base + l->fileofs);
4468         if (l->filelen % sizeof(*in))
4469                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4470         count = l->filelen / sizeof(*in);
4471         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4472
4473         loadmodel->brushq3.data_leafbrushes = out;
4474         loadmodel->brushq3.num_leafbrushes = count;
4475
4476         for (i = 0;i < count;i++, in++, out++)
4477         {
4478                 n = LittleLong(*in);
4479                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4480                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4481                 *out = loadmodel->brushq3.data_brushes + n;
4482         }
4483 }
4484
4485 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4486 {
4487         int *in;
4488         q3msurface_t **out;
4489         int i, n, count;
4490
4491         in = (void *)(mod_base + l->fileofs);
4492         if (l->filelen % sizeof(*in))
4493                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4494         count = l->filelen / sizeof(*in);
4495         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4496
4497         loadmodel->brushq3.data_leaffaces = out;
4498         loadmodel->brushq3.num_leaffaces = count;
4499
4500         for (i = 0;i < count;i++, in++, out++)
4501         {
4502                 n = LittleLong(*in);
4503                 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4504                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4505                 *out = loadmodel->brushq3.data_faces + n;
4506         }
4507 }
4508
4509 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4510 {
4511         q3dleaf_t *in;
4512         q3mleaf_t *out;
4513         int i, j, n, c, count;
4514
4515         in = (void *)(mod_base + l->fileofs);
4516         if (l->filelen % sizeof(*in))
4517                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4518         count = l->filelen / sizeof(*in);
4519         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4520
4521         loadmodel->brushq3.data_leafs = out;
4522         loadmodel->brushq3.num_leafs = count;
4523
4524         for (i = 0;i < count;i++, in++, out++)
4525         {
4526                 out->parent = NULL;
4527                 out->plane = NULL;
4528                 out->clusterindex = LittleLong(in->clusterindex);
4529                 out->areaindex = LittleLong(in->areaindex);
4530                 for (j = 0;j < 3;j++)
4531                 {
4532                         // yes the mins/maxs are ints
4533                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4534                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4535                 }
4536                 n = LittleLong(in->firstleafface);
4537                 c = LittleLong(in->numleaffaces);
4538                 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4539                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4540                 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4541                 out->numleaffaces = c;
4542                 n = LittleLong(in->firstleafbrush);
4543                 c = LittleLong(in->numleafbrushes);
4544                 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4545                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4546                 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4547                 out->numleafbrushes = c;
4548         }
4549 }
4550
4551 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4552 {
4553         if (node->parent)
4554                 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4555         node->parent = parent;
4556         if (node->plane)
4557         {
4558                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4559                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4560         }
4561 }
4562
4563 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4564 {
4565         q3dnode_t *in;
4566         q3mnode_t *out;
4567         int i, j, n, count;
4568
4569         in = (void *)(mod_base + l->fileofs);
4570         if (l->filelen % sizeof(*in))
4571                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4572         count = l->filelen / sizeof(*in);
4573         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4574
4575         loadmodel->brushq3.data_nodes = out;
4576         loadmodel->brushq3.num_nodes = count;
4577
4578         for (i = 0;i < count;i++, in++, out++)
4579         {
4580                 out->parent = NULL;
4581                 n = LittleLong(in->planeindex);
4582                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4583                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4584                 out->plane = loadmodel->brushq3.data_planes + n;
4585                 for (j = 0;j < 2;j++)
4586                 {
4587                         n = LittleLong(in->childrenindex[j]);
4588                         if (n >= 0)
4589                         {
4590                                 if (n >= loadmodel->brushq3.num_nodes)
4591                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4592                                 out->children[j] = loadmodel->brushq3.data_nodes + n;
4593                         }
4594                         else
4595                         {
4596                                 n = -1 - n;
4597                                 if (n >= loadmodel->brushq3.num_leafs)
4598                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4599                                 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4600                         }
4601                 }
4602                 for (j = 0;j < 3;j++)
4603                 {
4604                         // yes the mins/maxs are ints
4605                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4606                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4607                 }
4608         }
4609
4610         // set the parent pointers
4611         Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4612 }
4613
4614 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4615 {
4616         q3dlightgrid_t *in;
4617         q3dlightgrid_t *out;
4618         int count;
4619
4620         in = (void *)(mod_base + l->fileofs);
4621         if (l->filelen % sizeof(*in))
4622                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4623         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4624         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4625         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4626         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4627         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4628         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4629         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4630         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4631         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4632         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4633         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4634         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4635         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4636         if (l->filelen)
4637         {
4638                 if (l->filelen < count * (int)sizeof(*in))
4639                         Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)\n", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
4640                 if (l->filelen != count * (int)sizeof(*in))
4641                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4642         }
4643
4644         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4645         loadmodel->brushq3.data_lightgrid = out;
4646         loadmodel->brushq3.num_lightgrid = count;
4647
4648         // no swapping or validation necessary
4649         if (l->filelen)
4650                 memcpy(out, in, count * (int)sizeof(*out));
4651         else
4652         {
4653                 // no data, fill with white
4654                 int i;
4655                 for (i = 0;i < count;i++)
4656                 {
4657                         out[i].ambientrgb[0] = 128;
4658                         out[i].ambientrgb[1] = 128;
4659                         out[i].ambientrgb[2] = 128;
4660                         out[i].diffusergb[0] = 0;
4661                         out[i].diffusergb[1] = 0;
4662                         out[i].diffusergb[2] = 0;
4663                         out[i].diffusepitch = 0;
4664                         out[i].diffuseyaw = 0;
4665                 }
4666         }
4667
4668         Matrix4x4_CreateScale3(&loadmodel->brushq3.num_lightgrid_indexfromworld, loadmodel->brushq3.num_lightgrid_scale[0], loadmodel->brushq3.num_lightgrid_scale[1], loadmodel->brushq3.num_lightgrid_scale[2]);
4669         Matrix4x4_ConcatTranslate(&loadmodel->brushq3.num_lightgrid_indexfromworld, -loadmodel->brushq3.num_lightgrid_imins[0] * loadmodel->brushq3.num_lightgrid_cellsize[0], -loadmodel->brushq3.num_lightgrid_imins[1] * loadmodel->brushq3.num_lightgrid_cellsize[1], -loadmodel->brushq3.num_lightgrid_imins[2] * loadmodel->brushq3.num_lightgrid_cellsize[2]);
4670 }
4671
4672 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4673 {
4674         q3dpvs_t *in;
4675         int totalchains;
4676
4677         if (l->filelen == 0)
4678         {
4679                 int i;
4680                 // unvised maps often have cluster indices even without pvs, so check
4681                 // leafs to find real number of clusters
4682                 loadmodel->brush.num_pvsclusters = 1;
4683                 for (i = 0;i < loadmodel->brushq3.num_leafs;i++)
4684                         loadmodel->brush.num_pvsclusters = min(loadmodel->brush.num_pvsclusters, loadmodel->brushq3.data_leafs[i].clusterindex + 1);
4685
4686                 // create clusters
4687                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
4688                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4689                 loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4690                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
4691                 return;
4692         }
4693
4694         in = (void *)(mod_base + l->fileofs);
4695         if (l->filelen < 9)
4696                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4697
4698         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
4699         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
4700         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
4701                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
4702         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4703         if (l->filelen < totalchains + (int)sizeof(*in))
4704                 Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)\n", loadmodel->brush.num_pvsclusters, loadmodel->brush.num_pvsclusterbytes, totalchains + sizeof(*in), l->filelen);
4705
4706         loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4707         memcpy(loadmodel->brush.data_pvsclusters, (qbyte *)(in + 1), totalchains);
4708 }
4709
4710 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4711 {
4712         // FIXME: finish this code
4713         VectorCopy(in, out);
4714 }
4715
4716 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4717 {
4718         int i, j, k, index[3];
4719         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4720         q3dlightgrid_t *a, *s;
4721         // FIXME: write this
4722         if (!model->brushq3.num_lightgrid)
4723         {
4724                 ambientcolor[0] = 1;
4725                 ambientcolor[1] = 1;
4726                 ambientcolor[2] = 1;
4727                 return;
4728         }
4729         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4730         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4731         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4732         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4733         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4734         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4735         index[0] = (int)floor(transformed[0]);
4736         index[1] = (int)floor(transformed[1]);
4737         index[2] = (int)floor(transformed[2]);
4738         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4739         // now lerp the values
4740         VectorClear(diffusenormal);
4741         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4742         for (k = 0;k < 2;k++)
4743         {
4744                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4745                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4746                         continue;
4747                 for (j = 0;j < 2;j++)
4748                 {
4749                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4750                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4751                                 continue;
4752                         for (i = 0;i < 2;i++)
4753                         {
4754                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4755                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4756                                         continue;
4757                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4758                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4759                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4760                                 pitch = s->diffusepitch * M_PI / 128;
4761                                 yaw = s->diffuseyaw * M_PI / 128;
4762                                 sinpitch = sin(pitch);
4763                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4764                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4765                                 diffusenormal[2] += blend * (cos(pitch));
4766                                 //Con_Printf("blend %f: ambient %i %i %i, diffuse %i %i %i, diffusepitch %i diffuseyaw %i (%f %f, normal %f %f %f)\n", blend, s->ambientrgb[0], s->ambientrgb[1], s->ambientrgb[2], s->diffusergb[0], s->diffusergb[1], s->diffusergb[2], s->diffusepitch, s->diffuseyaw, pitch, yaw, (cos(yaw) * cospitch), (sin(yaw) * cospitch), (-sin(pitch)));
4767                         }
4768                 }
4769         }
4770         VectorNormalize(diffusenormal);
4771         //Con_Printf("result: ambient %f %f %f diffuse %f %f %f diffusenormal %f %f %f\n", ambientcolor[0], ambientcolor[1], ambientcolor[2], diffusecolor[0], diffusecolor[1], diffusecolor[2], diffusenormal[0], diffusenormal[1], diffusenormal[2]);
4772 }
4773
4774 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t point, int markframe)
4775 {
4776         int i;
4777         q3mleaf_t *leaf;
4778         colbrushf_t *brush;
4779         // find which leaf the point is in
4780         while (node->plane)
4781                 node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
4782         // point trace the brushes
4783         leaf = (q3mleaf_t *)node;
4784         for (i = 0;i < leaf->numleafbrushes;i++)
4785         {
4786                 brush = leaf->firstleafbrush[i]->colbrushf;
4787                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
4788                 {
4789                         brush->markframe = markframe;
4790                         Collision_TracePointBrushFloat(trace, point, leaf->firstleafbrush[i]->colbrushf);
4791                 }
4792         }
4793         // can't do point traces on curves (they have no thickness)
4794 }
4795
4796 static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t start, const vec3_t end, vec_t startfrac, vec_t endfrac, const vec3_t linestart, const vec3_t lineend, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs)
4797 {
4798         int i, startside, endside;
4799         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
4800         q3mleaf_t *leaf;
4801         q3msurface_t *face;
4802         colbrushf_t *brush;
4803         if (startfrac > trace->realfraction)
4804                 return;
4805         // note: all line fragments past first impact fraction are ignored
4806         if (VectorCompare(start, end))
4807         {
4808                 // find which leaf the point is in
4809                 while (node->plane)
4810                         node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist];
4811         }
4812         else
4813         {
4814                 // find which nodes the line is in and recurse for them
4815                 while (node->plane)
4816                 {
4817                         // recurse down node sides
4818                         dist1 = PlaneDiff(start, node->plane);
4819                         dist2 = PlaneDiff(end, node->plane);
4820                         startside = dist1 < 0;
4821                         endside = dist2 < 0;
4822                         if (startside == endside)
4823                         {
4824                                 // most of the time the line fragment is on one side of the plane
4825                                 node = node->children[startside];
4826                         }
4827                         else
4828                         {
4829                                 // line crosses node plane, split the line
4830                                 midfrac = dist1 / (dist1 - dist2);
4831                                 VectorLerp(start, midfrac, end, mid);
4832                                 // take the near side first
4833                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4834                                 if (midfrac <= trace->realfraction)
4835                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4836                                 return;
4837                         }
4838                 }
4839         }
4840         // hit a leaf
4841         nodesegmentmins[0] = min(start[0], end[0]);
4842         nodesegmentmins[1] = min(start[1], end[1]);
4843         nodesegmentmins[2] = min(start[2], end[2]);
4844         nodesegmentmaxs[0] = max(start[0], end[0]);
4845         nodesegmentmaxs[1] = max(start[1], end[1]);
4846         nodesegmentmaxs[2] = max(start[2], end[2]);
4847         // line trace the brushes
4848         leaf = (q3mleaf_t *)node;
4849         for (i = 0;i < leaf->numleafbrushes;i++)
4850         {
4851                 brush = leaf->firstleafbrush[i]->colbrushf;
4852                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4853                 {
4854                         brush->markframe = markframe;
4855                         Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4856                         if (startfrac > trace->realfraction)
4857                                 return;
4858                 }
4859         }
4860         // can't do point traces on curves (they have no thickness)
4861         if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
4862         {
4863                 // line trace the curves
4864                 for (i = 0;i < leaf->numleaffaces;i++)
4865                 {
4866                         face = leaf->firstleafface[i];
4867                         if (face->collisions && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4868                         {
4869                                 face->collisionmarkframe = markframe;
4870                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4871                                 if (startfrac > trace->realfraction)
4872                                         return;
4873                         }
4874                 }
4875         }
4876 }
4877
4878 static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs)
4879 {
4880         int i;
4881         //int sides;
4882         float nodesegmentmins[3], nodesegmentmaxs[3];
4883         q3mleaf_t *leaf;
4884         colbrushf_t *brush;
4885         q3msurface_t *face;
4886         /*
4887                 // find which nodes the line is in and recurse for them
4888                 while (node->plane)
4889                 {
4890                         // recurse down node sides
4891                         int startside, endside;
4892                         float dist1near, dist1far, dist2near, dist2far;
4893                         BoxPlaneCornerDistances(thisbrush_start->mins, thisbrush_start->maxs, node->plane, &dist1near, &dist1far);
4894                         BoxPlaneCornerDistances(thisbrush_end->mins, thisbrush_end->maxs, node->plane, &dist2near, &dist2far);
4895                         startside = dist1near < 0;
4896                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
4897                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
4898                         if (startside == 2 || endside == 2)
4899                         {
4900                                 // brushes cross plane
4901                                 // do not clip anything, just take both sides
4902                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4903                                 node = node->children[1];
4904                                 continue;
4905                         }
4906                         if (startside == 0)
4907                         {
4908                                 if (endside == 0)
4909                                 {
4910                                         node = node->children[0];
4911                                         continue;
4912                                 }
4913                                 else
4914                                 {
4915                                         //midf0 = dist1near / (dist1near - dist2near);
4916                                         //midf1 = dist1far / (dist1far - dist2far);
4917                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4918                                         node = node->children[1];
4919                                         continue;
4920                                 }
4921                         }
4922                         else
4923                         {
4924                                 if (endside == 0)
4925                                 {
4926                                         //midf0 = dist1near / (dist1near - dist2near);
4927                                         //midf1 = dist1far / (dist1far - dist2far);
4928                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4929                                         node = node->children[1];
4930                                         continue;
4931                                 }
4932                                 else
4933                                 {
4934                                         node = node->children[1];
4935                                         continue;
4936                                 }
4937                         }
4938
4939                         if (dist1near <  0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4940                         if (dist1near <  0 && dist2near <  0 && dist1far <  0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4941                         if (dist1near <  0 && dist2near <  0 && dist1far >= 0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4942                         if (dist1near <  0 && dist2near <  0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4943                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4944                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){}
4945                         if (dist1near <  0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4946                         if (dist1near <  0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4947                         if (dist1near >= 0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4948                         if (dist1near >= 0 && dist2near <  0 && dist1far <  0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4949                         if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far <  0){}
4950                         if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4951                         if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4952                         if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){node = node->children[0];continue;}
4953                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){node = node->children[0];continue;}
4954                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){node = node->children[0];continue;}
4955                         {             
4956                                 if (dist2near < 0) // d1n<0 && d2n<0
4957                                 {
4958                                         if (dist2near < 0) // d1n<0 && d2n<0
4959                                         {
4960                                                 if (dist2near < 0) // d1n<0 && d2n<0
4961                                                 {
4962                                                 }
4963                                                 else // d1n<0 && d2n>0
4964                                                 {
4965                                                 }
4966                                         }
4967                                         else // d1n<0 && d2n>0
4968                                         {
4969                                                 if (dist2near < 0) // d1n<0 && d2n<0
4970                                                 {
4971                                                 }
4972                                                 else // d1n<0 && d2n>0
4973                                                 {
4974                                                 }
4975                                         }
4976                                 }
4977                                 else // d1n<0 && d2n>0
4978                                 {
4979                                 }
4980                         }
4981                         else // d1n>0
4982                         {
4983                                 if (dist2near < 0) // d1n>0 && d2n<0
4984                                 {
4985                                 }
4986                                 else // d1n>0 && d2n>0
4987                                 {
4988                                 }
4989                         }
4990                         if (dist1near < 0 == dist1far < 0 == dist2near < 0 == dist2far < 0)
4991                         {
4992                                 node = node->children[startside];
4993                                 continue;
4994                         }
4995                         if (dist1near < dist2near)
4996                         {
4997                                 // out
4998                                 if (dist1near >= 0)
4999                                 {
5000                                         node = node->children[0];
5001                                         continue;
5002                                 }
5003                                 if (dist2far < 0)
5004                                 {
5005                                         node = node->children[1];
5006                                         continue;
5007                                 }
5008                                 // dist1near < 0 && dist2far >= 0
5009                         }
5010                         else
5011                         {
5012                                 // in
5013                         }
5014                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
5015                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
5016                         if (startside == 2 || endside == 2)
5017                         {
5018                                 // brushes cross plane
5019                                 // do not clip anything, just take both sides
5020                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5021                                 node = node->children[1];
5022                         }
5023                         else if (startside == endside)
5024                                 node = node->children[startside];
5025                         else if (startside == 0) // endside = 1 (start infront, end behind)
5026                         {
5027                         }
5028                         else // startside == 1 endside = 0 (start behind, end infront)
5029                         {
5030                         }
5031                         == endside)
5032                         {
5033                                 if (startside < 2)
5034                                         node = node->children[startside];
5035                                 else
5036                                 {
5037                                         // start and end brush cross plane
5038                                 }
5039                         }
5040                         else
5041                         {
5042                         }
5043                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5044                                 node = node->children[1];
5045                         else if (dist1near < 0 && dist1far < 0 && dist2near >= 0 && dist2far >= 0)
5046                         else if (dist1near >= 0 && dist1far >= 0 && dist2near < 0 && dist2far < 0)
5047                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5048                                 node = node->children[0];
5049                         else
5050                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5051                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5052                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5053                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5054                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5055                         {
5056                         }
5057                         else if (dist1near >= 0 && dist1far >= 0)
5058                         {
5059                         }
5060                         else // mixed (lying on plane)
5061                         {
5062                         }
5063                         {
5064                                 if (dist2near < 0 && dist2far < 0)
5065                                 {
5066                                 }
5067                                 else
5068                                         node = node->children[1];
5069                         }
5070                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5071                                 node = node->children[0];
5072                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5073                                 node = node->children[1];
5074                         else
5075                         {
5076                                 // both sides
5077                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5078                                 node = node->children[1];
5079                         }
5080                         sides = dist1near || dist1near < 0 | dist1far < 0 | dist2near < 0 | dist
5081                         startside = dist1 < 0;
5082                         endside = dist2 < 0;
5083                         if (startside == endside)
5084                         {
5085                                 // most of the time the line fragment is on one side of the plane
5086                                 node = node->children[startside];
5087                         }
5088                         else
5089                         {
5090                                 // line crosses node plane, split the line
5091                                 midfrac = dist1 / (dist1 - dist2);
5092                                 VectorLerp(start, midfrac, end, mid);
5093                                 // take the near side first
5094                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5095                                 if (midfrac <= trace->fraction)
5096                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5097                                 return;
5098                         }
5099                 }
5100         */
5101 #if 1
5102         for (;;)
5103         {
5104                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5105                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5106                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5107                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5108                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5109                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5110                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5111                         return;
5112                 if (!node->plane)
5113                         break;
5114                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5115                 node = node->children[1];
5116         }
5117 #elif 0
5118         // FIXME: could be made faster by copying TraceLine code and making it use
5119         // box plane distances...  (variant on the BoxOnPlaneSide code)
5120         for (;;)
5121         {
5122                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5123                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5124                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5125                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5126                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5127                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5128                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5129                         return;
5130                 if (!node->plane)
5131                         break;
5132                 if (mod_q3bsp_debugtracebrush.integer == 2)
5133                 {
5134                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5135                         node = node->children[1];
5136                         continue;
5137                 }
5138                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5139                 {
5140                         // recurse down node sides
5141                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5142                         if (sides == 3)
5143                         {
5144                                 // segment box crosses plane
5145                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5146                                 node = node->children[1];
5147                                 continue;
5148                         }
5149                         // take whichever side the segment box is on
5150                         node = node->children[sides - 1];
5151                         continue;
5152                 }
5153                 else
5154                 {
5155                         // recurse down node sides
5156                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5157                         if (sides == 3)
5158                         {
5159                                 // segment box crosses plane
5160                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5161                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5162                                 if (sides == 3)
5163                                 {
5164                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5165                                         node = node->children[1];
5166                                         continue;
5167                                 }
5168                         }
5169                         // take whichever side the segment box is on
5170                         node = node->children[sides - 1];
5171                         continue;
5172                 }
5173                 return;
5174         }
5175 #else
5176         // FIXME: could be made faster by copying TraceLine code and making it use
5177         // box plane distances...  (variant on the BoxOnPlaneSide code)
5178         for (;;)
5179         {
5180                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5181                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5182                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5183                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5184                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5185                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5186                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5187                         return;
5188                 if (!node->plane)
5189                         break;
5190                 if (mod_q3bsp_debugtracebrush.integer == 2)
5191                 {
5192                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5193                         node = node->children[1];
5194                 }
5195                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5196                 {
5197                         // recurse down node sides
5198                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5199                         if (sides == 3)
5200                         {
5201                                 // segment box crosses plane
5202                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5203                                 node = node->children[1];
5204                         }
5205                         else
5206                         {
5207                                 // take whichever side the segment box is on
5208                                 node = node->children[sides - 1];
5209                         }
5210                 }
5211                 else
5212                 {
5213                         // recurse down node sides
5214                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5215                         if (sides == 3)
5216                         {
5217                                 // segment box crosses plane
5218                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5219                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5220                                 if (sides == 3)
5221                                 {
5222                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5223                                         sides = 2;
5224                                 }
5225                         }
5226                         // take whichever side the segment box is on
5227                         node = node->children[sides - 1];
5228                 }
5229         }
5230 #endif
5231         // hit a leaf
5232         leaf = (q3mleaf_t *)node;
5233         for (i = 0;i < leaf->numleafbrushes;i++)
5234         {
5235                 brush = leaf->firstleafbrush[i]->colbrushf;
5236                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5237                 {
5238                         brush->markframe = markframe;
5239                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
5240                 }
5241         }
5242         if (mod_q3bsp_curves_collisions.integer)
5243         {
5244                 for (i = 0;i < leaf->numleaffaces;i++)
5245                 {
5246                         face = leaf->firstleafface[i];
5247                         if (face->collisions && face->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
5248                         {
5249                                 face->markframe = markframe;
5250                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5251                         }
5252                 }
5253         }
5254 }
5255
5256 static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
5257 {
5258         int i;
5259         float segmentmins[3], segmentmaxs[3];
5260         colbrushf_t *thisbrush_start, *thisbrush_end;
5261         matrix4x4_t startmatrix, endmatrix;
5262         static int markframe = 0;
5263         q3msurface_t *face;
5264         memset(trace, 0, sizeof(*trace));
5265         trace->fraction = 1;
5266         trace->realfraction = 1;
5267         trace->hitsupercontentsmask = hitsupercontentsmask;
5268         Matrix4x4_CreateIdentity(&startmatrix);
5269         Matrix4x4_CreateIdentity(&endmatrix);
5270         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
5271         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
5272         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
5273         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
5274         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
5275         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
5276         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
5277         {
5278                 if (VectorCompare(boxstartmins, boxendmins))
5279                 {
5280                         // point trace
5281                         if (model->brushq3.submodel)
5282                         {
5283                                 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5284                                         if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5285                                                 Collision_TracePointBrushFloat(trace, boxstartmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5286                         }
5287                         else
5288                                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, ++markframe);
5289                 }
5290                 else
5291                 {
5292                         // line trace
5293                         if (model->brushq3.submodel)
5294                         {
5295                                 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5296                                         if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5297                                                 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5298                                 if (mod_q3bsp_curves_collisions.integer)
5299                                 {
5300                                         for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
5301                                         {
5302                                                 face = model->brushq3.data_thismodel->firstface + i;
5303                                                 if (face->collisions)
5304                                                         Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5305                                         }
5306                                 }
5307                         }
5308                         else
5309                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe, segmentmins, segmentmaxs);
5310                 }
5311         }
5312         else
5313         {
5314                 // box trace, performed as brush trace
5315                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
5316                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
5317                 if (model->brushq3.submodel)
5318                 {
5319                         for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5320                                 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5321                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5322                         if (mod_q3bsp_curves_collisions.integer)
5323                         {
5324                                 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
5325                                 {
5326                                         face = model->brushq3.data_thismodel->firstface + i;
5327                                         if (face->collisions)
5328                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5329                                 }
5330                         }
5331                 }
5332                 else
5333                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
5334         }
5335 }
5336
5337 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
5338 {
5339         int clusterindex, side, nodestackindex = 0;
5340         q3mnode_t *node, *nodestack[1024];
5341         node = model->brushq3.data_nodes;
5342         if (!model->brush.num_pvsclusters)
5343                 return true;
5344         for (;;)
5345         {
5346                 if (node->plane)
5347                 {
5348                         // node - recurse down the BSP tree
5349                         side = BoxOnPlaneSide(mins, maxs, node->plane) - 1;
5350                         if (side < 2)
5351                         {
5352                                 // box is on one side of plane, take that path
5353                                 node = node->children[side];
5354                         }
5355                         else
5356                         {
5357                                 // box crosses plane, take one path and remember the other
5358                                 if (nodestackindex < 1024)
5359                                         nodestack[nodestackindex++] = node->children[0];
5360                                 node = node->children[1];
5361                         }
5362                 }
5363                 else
5364                 {
5365                         // leaf - check cluster bit
5366                         clusterindex = ((q3mleaf_t *)node)->clusterindex;
5367 #if 0
5368                         if (clusterindex >= model->brush.num_pvsclusters)
5369                         {
5370                                 Con_Printf("%i >= %i\n", clusterindex, model->brush.num_pvsclusters);
5371                                 return true;
5372                         }
5373 #endif
5374                         if (CHECKPVSBIT(pvs, clusterindex))
5375                         {
5376                                 // it is visible, return immediately with the news
5377                                 return true;
5378                         }
5379                         else
5380                         {
5381                                 // nothing to see here, try another path we didn't take earlier
5382                                 if (nodestackindex == 0)
5383                                         break;
5384                                 node = nodestack[--nodestackindex];
5385                         }
5386                 }
5387         }
5388         // it is not visible
5389         return false;
5390 }
5391
5392 //Returns PVS data for a given point
5393 //(note: can return NULL)
5394 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
5395 {
5396         q3mnode_t *node;
5397         Mod_CheckLoaded(model);
5398         node = model->brushq3.data_nodes;
5399         while (node->plane)
5400                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
5401         if (((q3mleaf_t *)node)->clusterindex >= 0)
5402                 return model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5403         else
5404                 return NULL;
5405 }
5406
5407 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
5408 {
5409         while (node->plane)
5410         {
5411                 float d = PlaneDiff(org, node->plane);
5412                 if (d > radius)
5413                         node = node->children[0];
5414                 else if (d < -radius)
5415                         node = node->children[1];
5416                 else
5417                 {
5418                         // go down both sides
5419                         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
5420                         node = node->children[1];
5421                 }
5422         }
5423         // if this leaf is in a cluster, accumulate the pvs bits
5424         if (((q3mleaf_t *)node)->clusterindex >= 0)
5425         {
5426                 int i;
5427                 qbyte *pvs = model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5428                 for (i = 0;i < pvsbytes;i++)
5429                         pvsbuffer[i] |= pvs[i];
5430         }
5431 }
5432
5433 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
5434 //of the given point.
5435 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
5436 {
5437         int bytes = model->brush.num_pvsclusterbytes;
5438         bytes = min(bytes, pvsbufferlength);
5439         if (r_novis.integer || !model->brush.num_pvsclusters || !Mod_Q3BSP_GetPVS(model, org))
5440         {
5441                 memset(pvsbuffer, 0xFF, bytes);
5442                 return bytes;
5443         }
5444         memset(pvsbuffer, 0, bytes);
5445         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
5446         return bytes;
5447 }
5448
5449
5450 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
5451 {
5452         int supercontents = 0;
5453         if (nativecontents & Q2CONTENTS_SOLID)
5454                 supercontents |= SUPERCONTENTS_SOLID;
5455         if (nativecontents & Q2CONTENTS_WATER)
5456                 supercontents |= SUPERCONTENTS_WATER;
5457         if (nativecontents & Q2CONTENTS_SLIME)
5458                 supercontents |= SUPERCONTENTS_SLIME;
5459         if (nativecontents & Q2CONTENTS_LAVA)
5460                 supercontents |= SUPERCONTENTS_LAVA;
5461         return supercontents;
5462 }
5463
5464 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
5465 {
5466         int nativecontents = 0;
5467         if (supercontents & SUPERCONTENTS_SOLID)
5468                 nativecontents |= Q2CONTENTS_SOLID;
5469         if (supercontents & SUPERCONTENTS_WATER)
5470                 nativecontents |= Q2CONTENTS_WATER;
5471         if (supercontents & SUPERCONTENTS_SLIME)
5472                 nativecontents |= Q2CONTENTS_SLIME;
5473         if (supercontents & SUPERCONTENTS_LAVA)
5474                 nativecontents |= Q2CONTENTS_LAVA;
5475         return nativecontents;
5476 }
5477
5478 /*
5479 void Mod_Q3BSP_RecursiveGetVisible(q3mnode_t *node, model_t *model, const vec3_t point, const vec3_t mins, const vec3_t maxs, int maxleafs, q3mleaf_t *leaflist, int *numleafs, int maxsurfaces, q3msurface_t *surfacelist, int *numsurfaces, const qbyte *pvs)
5480 {
5481         mleaf_t *leaf;
5482         for (;;)
5483         {
5484                 if (!BoxesOverlap(node->mins, node->maxs, mins, maxs))
5485                         return;
5486                 if (!node->plane)
5487                         break;
5488                 Mod_Q3BSP_RecursiveGetVisible(node->children[0], model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
5489                 node = node->children[1];
5490         }
5491         leaf = (mleaf_t *)node;
5492         if ((pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
5493         {
5494                 int marksurfacenum;
5495                 q3msurface_t *surf;
5496                 if (maxleafs && *numleafs < maxleafs)
5497                         leaflist[(*numleaf)++] = leaf;
5498                 if (maxsurfaces)
5499                 {
5500                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
5501                         {
5502                                 face = leaf->firstleafface[marksurfacenum];
5503                                 if (face->shadowmark != shadowmarkcount)
5504                                 {
5505                                         face->shadowmark = shadowmarkcount;
5506                                         if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces)
5507                                                 surfacelist[(*numsurfaces)++] = face;
5508                                 }
5509                         }
5510                 }
5511         }
5512 }
5513
5514 void Mod_Q3BSP_GetVisible(model_t *model, const vec3_t point, const vec3_t mins, const vec3_t maxs, int maxleafs, q3mleaf_t *leaflist, int *numleafs, int maxsurfaces, q3msurface_t *surfacelist, int *numsurfaces)
5515 {
5516         // FIXME: support portals
5517         if (maxsurfaces)
5518                 *numsurfaces = 0;
5519         if (maxleafs)
5520                 *numleafs = 0;
5521         if (model->submodel)
5522         {
5523                 if (maxsurfaces)
5524                 {
5525                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
5526                         {
5527                                 face = ent->model->brushq3.surfaces + leaf->firstmarksurface[marksurfacenum];
5528                                 if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces)
5529                                         surfacelist[(*numsurfaces)++] = face;
5530                         }
5531                 }
5532         }
5533         else
5534         {
5535                 pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
5536                 Mod_Q3BSP_RecursiveGetVisible(ent->model->brushq3.data_nodes, model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
5537         }
5538 }
5539 */
5540
5541 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
5542 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
5543 extern void R_Q3BSP_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);
5544 extern void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
5545 extern void R_Q3BSP_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, int numsurfaces, const int *surfacelist);
5546 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
5547 {
5548         int i, j, numshadowmeshtriangles;
5549         q3dheader_t *header;
5550         float corner[3], yawradius, modelradius;
5551         q3msurface_t *face;
5552
5553         mod->type = mod_brushq3;
5554         mod->numframes = 1;
5555         mod->numskins = 1;
5556
5557         header = (q3dheader_t *)buffer;
5558
5559         i = LittleLong(header->version);
5560         if (i != Q3BSPVERSION)
5561                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
5562         if (mod->isworldmodel)
5563         {
5564                 Cvar_SetValue("halflifebsp", false);
5565                 // until we get a texture for it...
5566                 R_ResetQuakeSky();
5567         }
5568
5569         mod->soundfromcenter = true;
5570         mod->TraceBox = Mod_Q3BSP_TraceBox;
5571         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
5572         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
5573         mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
5574         mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
5575         mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
5576         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
5577         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5578         //mod->DrawSky = R_Q3BSP_DrawSky;
5579         mod->Draw = R_Q3BSP_Draw;
5580         mod->GetLightInfo = R_Q3BSP_GetLightInfo;
5581         mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
5582         mod->DrawLight = R_Q3BSP_DrawLight;
5583
5584         mod_base = (qbyte *)header;
5585
5586         // swap all the lumps
5587         header->ident = LittleLong(header->ident);
5588         header->version = LittleLong(header->version);
5589         for (i = 0;i < Q3HEADER_LUMPS;i++)
5590         {
5591                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
5592                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
5593         }
5594
5595         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
5596         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
5597         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
5598         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
5599         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
5600         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
5601         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
5602         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
5603         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
5604         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
5605         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
5606         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
5607         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
5608         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
5609         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
5610         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
5611         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
5612         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
5613
5614         // make a single combined shadow mesh to allow optimized shadow volume creation
5615         numshadowmeshtriangles = 0;
5616         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5617         {
5618                 face->num_firstshadowmeshtriangle = numshadowmeshtriangles;
5619                 numshadowmeshtriangles += face->num_triangles;
5620         }
5621         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
5622         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5623                 Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
5624         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
5625         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
5626         
5627         for (i = 0;i < loadmodel->brushq3.num_models;i++)
5628         {
5629                 if (i == 0)
5630                         mod = loadmodel;
5631                 else
5632                 {
5633                         char name[10];
5634                         // LordHavoc: only register submodels if it is the world
5635                         // (prevents bsp models from replacing world submodels)
5636                         if (!loadmodel->isworldmodel)
5637                                 continue;
5638                         // duplicate the basic information
5639                         sprintf(name, "*%i", i);
5640                         mod = Mod_FindName(name);
5641                         *mod = *loadmodel;
5642                         strcpy(mod->name, name);
5643                         // textures and memory belong to the main model
5644                         mod->texturepool = NULL;
5645                         mod->mempool = NULL;
5646                         mod->brush.GetPVS = NULL;
5647                         mod->brush.FatPVS = NULL;
5648                         mod->brush.BoxTouchingPVS = NULL;
5649                         mod->brush.LightPoint = NULL;
5650                         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5651                 }
5652                 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
5653                 mod->brushq3.submodel = i;
5654
5655                 // make the model surface list (used by shadowing/lighting)
5656                 mod->nummodelsurfaces = mod->brushq3.data_thismodel->numfaces;
5657                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
5658                 for (j = 0;j < mod->nummodelsurfaces;j++)
5659                         mod->surfacelist[j] = (mod->brushq3.data_thismodel->firstface - mod->brushq3.data_faces) + j;
5660
5661                 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
5662                 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
5663                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
5664                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
5665                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
5666                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
5667                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
5668                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
5669                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
5670                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
5671                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
5672                 mod->yawmins[2] = mod->normalmins[2];
5673                 mod->yawmaxs[2] = mod->normalmaxs[2];
5674                 mod->radius = modelradius;
5675                 mod->radius2 = modelradius * modelradius;
5676
5677                 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
5678                         if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
5679                                 break;
5680                 if (j < mod->brushq3.data_thismodel->numfaces)
5681                         mod->DrawSky = R_Q3BSP_DrawSky;
5682         }
5683 }
5684
5685 void Mod_IBSP_Load(model_t *mod, void *buffer)
5686 {
5687         int i = LittleLong(((int *)buffer)[1]);
5688         if (i == Q3BSPVERSION)
5689                 Mod_Q3BSP_Load(mod,buffer);
5690         else if (i == Q2BSPVERSION)
5691                 Mod_Q2BSP_Load(mod,buffer);
5692         else
5693                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
5694 }
5695
5696 void Mod_MAP_Load(model_t *mod, void *buffer)
5697 {
5698         Host_Error("Mod_MAP_Load: not yet implemented\n");
5699 }
5700