]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
make CL_NextDemo put up menu if there's no demo to play (this makes startdemos comman...
[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_brush;
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         for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2926                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2927
2928 // load into heap
2929
2930         // store which lightmap format to use
2931         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2932
2933         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2934         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2935         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2936         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2937         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2938         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2939         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2940         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2941         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2942         Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2943         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2944         // load submodels before leafs because they contain the number of vis leafs
2945         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2946         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2947         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2948         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2949
2950         if (!mod->brushq1.lightdata)
2951                 mod->brush.LightPoint = NULL;
2952
2953         if (mod->brushq1.data_compressedpvs)
2954                 Mem_Free(mod->brushq1.data_compressedpvs);
2955         mod->brushq1.data_compressedpvs = NULL;
2956         mod->brushq1.num_compressedpvs = 0;
2957
2958         Mod_Q1BSP_MakeHull0();
2959         Mod_Q1BSP_MakePortals();
2960
2961         mod->numframes = 2;             // regular and alternate animation
2962
2963         mainmempool = mod->mempool;
2964
2965         Mod_Q1BSP_LoadLightList();
2966         loadmodel = loadmodel;
2967
2968         // make a single combined shadow mesh to allow optimized shadow volume creation
2969         numshadowmeshtriangles = 0;
2970         for (j = 0, surf = loadmodel->brushq1.surfaces;j < loadmodel->brushq1.numsurfaces;j++, surf++)
2971         {
2972                 surf->num_firstshadowmeshtriangle = numshadowmeshtriangles;
2973                 numshadowmeshtriangles += surf->mesh.num_triangles;
2974         }
2975         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
2976         for (j = 0, surf = loadmodel->brushq1.surfaces;j < loadmodel->brushq1.numsurfaces;j++, surf++)
2977                 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);
2978         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
2979         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
2980         
2981         // LordHavoc: to clear the fog around the original quake submodel code, I
2982         // will explain:
2983         // first of all, some background info on the submodels:
2984         // model 0 is the map model (the world, named maps/e1m1.bsp for example)
2985         // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
2986         // now the weird for loop itself:
2987         // the loop functions in an odd way, on each iteration it sets up the
2988         // current 'mod' model (which despite the confusing code IS the model of
2989         // the number i), at the end of the loop it duplicates the model to become
2990         // the next submodel, and loops back to set up the new submodel.
2991
2992         // LordHavoc: now the explanation of my sane way (which works identically):
2993         // set up the world model, then on each submodel copy from the world model
2994         // and set up the submodel with the respective model info.
2995         for (i = 0;i < mod->brush.numsubmodels;i++)
2996         {
2997                 // LordHavoc: this code was originally at the end of this loop, but
2998                 // has been transformed to something more readable at the start here.
2999
3000                 // LordHavoc: only register submodels if it is the world
3001                 // (prevents external bsp models from replacing world submodels with
3002                 //  their own)
3003                 if (loadmodel->isworldmodel && i)
3004                 {
3005                         char name[10];
3006                         // duplicate the basic information
3007                         sprintf(name, "*%i", i);
3008                         mod = Mod_FindName(name);
3009                         // copy the base model to this one
3010                         *mod = *loadmodel;
3011                         // rename the clone back to its proper name
3012                         strcpy(mod->name, name);
3013                         // textures and memory belong to the main model
3014                         mod->texturepool = NULL;
3015                         mod->mempool = NULL;
3016                 }
3017
3018                 bm = &mod->brushq1.submodels[i];
3019
3020                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3021                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3022                 {
3023                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3024                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3025                 }
3026
3027                 mod->firstmodelsurface = bm->firstface;
3028                 mod->nummodelsurfaces = bm->numfaces;
3029
3030                 // make the model surface list (used by shadowing/lighting)
3031                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
3032                 for (j = 0;j < mod->nummodelsurfaces;j++)
3033                         mod->surfacelist[j] = mod->firstmodelsurface + j;
3034
3035                 // this gets altered below if sky is used
3036                 mod->DrawSky = NULL;
3037                 mod->Draw = R_Q1BSP_Draw;
3038                 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3039                 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3040                 mod->DrawLight = R_Q1BSP_DrawLight;
3041                 if (i != 0)
3042                 {
3043                         mod->brush.GetPVS = NULL;
3044                         mod->brush.FatPVS = NULL;
3045                         mod->brush.BoxTouchingPVS = NULL;
3046                         mod->brush.LightPoint = NULL;
3047                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3048                 }
3049                 mod->brushq1.pvstexturechains = Mem_Alloc(loadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
3050                 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(loadmodel->mempool,(mod->nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
3051                 mod->brushq1.pvstexturechainslength = Mem_Alloc(loadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
3052                 Mod_Q1BSP_BuildPVSTextureChains(mod);
3053                 Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
3054                 if (mod->nummodelsurfaces)
3055                 {
3056                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3057                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3058                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3059                         modelyawradius = 0;
3060                         modelradius = 0;
3061                         for (j = 0, surf = &mod->brushq1.surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
3062                         {
3063                                 // we only need to have a drawsky function if it is used(usually only on world model)
3064                                 if (surf->texinfo->texture->flags & SURF_DRAWSKY)
3065                                         mod->DrawSky = R_Q1BSP_DrawSky;
3066                                 // LordHavoc: submodels always clip, even if water
3067                                 if (mod->brush.numsubmodels - 1)
3068                                         surf->flags |= SURF_SOLIDCLIP;
3069                                 // calculate bounding shapes
3070                                 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
3071                                 {
3072                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3073                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3074                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3075                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3076                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3077                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3078                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3079                                         if (modelyawradius < dist)
3080                                                 modelyawradius = dist;
3081                                         dist += vec[2]*vec[2];
3082                                         if (modelradius < dist)
3083                                                 modelradius = dist;
3084                                 }
3085                         }
3086                         modelyawradius = sqrt(modelyawradius);
3087                         modelradius = sqrt(modelradius);
3088                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3089                         mod->yawmins[2] = mod->normalmins[2];
3090                         mod->yawmaxs[2] = mod->normalmaxs[2];
3091                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3092                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3093                         mod->radius = modelradius;
3094                         mod->radius2 = modelradius * modelradius;
3095                 }
3096                 else
3097                 {
3098                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3099                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3100                 }
3101                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->firstmodelsurface, mod->nummodelsurfaces, loadmodel->mempool);
3102
3103                 mod->brushq1.num_visleafs = bm->visleafs;
3104         }
3105
3106         //Mod_Q1BSP_ProcessLightList();
3107
3108         if (developer.integer)
3109                 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);
3110 }
3111
3112 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3113 {
3114 }
3115
3116 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3117 {
3118 /*
3119         d_t *in;
3120         m_t *out;
3121         int i, count;
3122
3123         in = (void *)(mod_base + l->fileofs);
3124         if (l->filelen % sizeof(*in))
3125                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3126         count = l->filelen / sizeof(*in);
3127         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3128
3129         loadmodel-> = out;
3130         loadmodel->num = count;
3131
3132         for (i = 0;i < count;i++, in++, out++)
3133         {
3134         }
3135 */
3136 }
3137
3138 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3139 {
3140 /*
3141         d_t *in;
3142         m_t *out;
3143         int i, count;
3144
3145         in = (void *)(mod_base + l->fileofs);
3146         if (l->filelen % sizeof(*in))
3147                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3148         count = l->filelen / sizeof(*in);
3149         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3150
3151         loadmodel-> = out;
3152         loadmodel->num = count;
3153
3154         for (i = 0;i < count;i++, in++, out++)
3155         {
3156         }
3157 */
3158 }
3159
3160 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3161 {
3162 /*
3163         d_t *in;
3164         m_t *out;
3165         int i, count;
3166
3167         in = (void *)(mod_base + l->fileofs);
3168         if (l->filelen % sizeof(*in))
3169                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3170         count = l->filelen / sizeof(*in);
3171         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3172
3173         loadmodel-> = out;
3174         loadmodel->num = count;
3175
3176         for (i = 0;i < count;i++, in++, out++)
3177         {
3178         }
3179 */
3180 }
3181
3182 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3183 {
3184 /*
3185         d_t *in;
3186         m_t *out;
3187         int i, count;
3188
3189         in = (void *)(mod_base + l->fileofs);
3190         if (l->filelen % sizeof(*in))
3191                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3192         count = l->filelen / sizeof(*in);
3193         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3194
3195         loadmodel-> = out;
3196         loadmodel->num = count;
3197
3198         for (i = 0;i < count;i++, in++, out++)
3199         {
3200         }
3201 */
3202 }
3203
3204 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3205 {
3206 /*
3207         d_t *in;
3208         m_t *out;
3209         int i, count;
3210
3211         in = (void *)(mod_base + l->fileofs);
3212         if (l->filelen % sizeof(*in))
3213                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3214         count = l->filelen / sizeof(*in);
3215         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3216
3217         loadmodel-> = out;
3218         loadmodel->num = count;
3219
3220         for (i = 0;i < count;i++, in++, out++)
3221         {
3222         }
3223 */
3224 }
3225
3226 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3227 {
3228 /*
3229         d_t *in;
3230         m_t *out;
3231         int i, count;
3232
3233         in = (void *)(mod_base + l->fileofs);
3234         if (l->filelen % sizeof(*in))
3235                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3236         count = l->filelen / sizeof(*in);
3237         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3238
3239         loadmodel-> = out;
3240         loadmodel->num = count;
3241
3242         for (i = 0;i < count;i++, in++, out++)
3243         {
3244         }
3245 */
3246 }
3247
3248 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3249 {
3250 /*
3251         d_t *in;
3252         m_t *out;
3253         int i, count;
3254
3255         in = (void *)(mod_base + l->fileofs);
3256         if (l->filelen % sizeof(*in))
3257                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3258         count = l->filelen / sizeof(*in);
3259         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3260
3261         loadmodel-> = out;
3262         loadmodel->num = count;
3263
3264         for (i = 0;i < count;i++, in++, out++)
3265         {
3266         }
3267 */
3268 }
3269
3270 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3271 {
3272 /*
3273         d_t *in;
3274         m_t *out;
3275         int i, count;
3276
3277         in = (void *)(mod_base + l->fileofs);
3278         if (l->filelen % sizeof(*in))
3279                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3280         count = l->filelen / sizeof(*in);
3281         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3282
3283         loadmodel-> = out;
3284         loadmodel->num = count;
3285
3286         for (i = 0;i < count;i++, in++, out++)
3287         {
3288         }
3289 */
3290 }
3291
3292 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3293 {
3294 /*
3295         d_t *in;
3296         m_t *out;
3297         int i, count;
3298
3299         in = (void *)(mod_base + l->fileofs);
3300         if (l->filelen % sizeof(*in))
3301                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3302         count = l->filelen / sizeof(*in);
3303         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3304
3305         loadmodel-> = out;
3306         loadmodel->num = count;
3307
3308         for (i = 0;i < count;i++, in++, out++)
3309         {
3310         }
3311 */
3312 }
3313
3314 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3315 {
3316 /*
3317         d_t *in;
3318         m_t *out;
3319         int i, count;
3320
3321         in = (void *)(mod_base + l->fileofs);
3322         if (l->filelen % sizeof(*in))
3323                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3324         count = l->filelen / sizeof(*in);
3325         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3326
3327         loadmodel-> = out;
3328         loadmodel->num = count;
3329
3330         for (i = 0;i < count;i++, in++, out++)
3331         {
3332         }
3333 */
3334 }
3335
3336 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3337 {
3338 /*
3339         d_t *in;
3340         m_t *out;
3341         int i, count;
3342
3343         in = (void *)(mod_base + l->fileofs);
3344         if (l->filelen % sizeof(*in))
3345                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3346         count = l->filelen / sizeof(*in);
3347         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3348
3349         loadmodel-> = out;
3350         loadmodel->num = count;
3351
3352         for (i = 0;i < count;i++, in++, out++)
3353         {
3354         }
3355 */
3356 }
3357
3358 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3359 {
3360 /*
3361         d_t *in;
3362         m_t *out;
3363         int i, count;
3364
3365         in = (void *)(mod_base + l->fileofs);
3366         if (l->filelen % sizeof(*in))
3367                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3368         count = l->filelen / sizeof(*in);
3369         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3370
3371         loadmodel-> = out;
3372         loadmodel->num = count;
3373
3374         for (i = 0;i < count;i++, in++, out++)
3375         {
3376         }
3377 */
3378 }
3379
3380 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3381 {
3382 /*
3383         d_t *in;
3384         m_t *out;
3385         int i, count;
3386
3387         in = (void *)(mod_base + l->fileofs);
3388         if (l->filelen % sizeof(*in))
3389                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3390         count = l->filelen / sizeof(*in);
3391         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3392
3393         loadmodel-> = out;
3394         loadmodel->num = count;
3395
3396         for (i = 0;i < count;i++, in++, out++)
3397         {
3398         }
3399 */
3400 }
3401
3402 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3403 {
3404 /*
3405         d_t *in;
3406         m_t *out;
3407         int i, count;
3408
3409         in = (void *)(mod_base + l->fileofs);
3410         if (l->filelen % sizeof(*in))
3411                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3412         count = l->filelen / sizeof(*in);
3413         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3414
3415         loadmodel-> = out;
3416         loadmodel->num = count;
3417
3418         for (i = 0;i < count;i++, in++, out++)
3419         {
3420         }
3421 */
3422 }
3423
3424 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3425 {
3426 /*
3427         d_t *in;
3428         m_t *out;
3429         int i, count;
3430
3431         in = (void *)(mod_base + l->fileofs);
3432         if (l->filelen % sizeof(*in))
3433                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3434         count = l->filelen / sizeof(*in);
3435         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3436
3437         loadmodel-> = out;
3438         loadmodel->num = count;
3439
3440         for (i = 0;i < count;i++, in++, out++)
3441         {
3442         }
3443 */
3444 }
3445
3446 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3447 {
3448 /*
3449         d_t *in;
3450         m_t *out;
3451         int i, count;
3452
3453         in = (void *)(mod_base + l->fileofs);
3454         if (l->filelen % sizeof(*in))
3455                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3456         count = l->filelen / sizeof(*in);
3457         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3458
3459         loadmodel-> = out;
3460         loadmodel->num = count;
3461
3462         for (i = 0;i < count;i++, in++, out++)
3463         {
3464         }
3465 */
3466 }
3467
3468 static void Mod_Q2BSP_LoadModels(lump_t *l)
3469 {
3470 /*
3471         d_t *in;
3472         m_t *out;
3473         int i, count;
3474
3475         in = (void *)(mod_base + l->fileofs);
3476         if (l->filelen % sizeof(*in))
3477                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3478         count = l->filelen / sizeof(*in);
3479         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3480
3481         loadmodel-> = out;
3482         loadmodel->num = count;
3483
3484         for (i = 0;i < count;i++, in++, out++)
3485         {
3486         }
3487 */
3488 }
3489
3490 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3491 {
3492         int i;
3493         q2dheader_t *header;
3494
3495         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3496
3497         mod->type = mod_brushq2;
3498
3499         header = (q2dheader_t *)buffer;
3500
3501         i = LittleLong(header->version);
3502         if (i != Q2BSPVERSION)
3503                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3504         mod->brush.ishlbsp = false;
3505         if (loadmodel->isworldmodel)
3506         {
3507                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3508                 // until we get a texture for it...
3509                 R_ResetQuakeSky();
3510         }
3511
3512         mod_base = (qbyte *)header;
3513
3514         // swap all the lumps
3515         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3516                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3517
3518         // store which lightmap format to use
3519         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3520
3521         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3522         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3523         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3524         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3525         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3526         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3527         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3528         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3529         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3530         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3531         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3532         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3533         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3534         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3535         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3536         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3537         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3538         // LordHavoc: must go last because this makes the submodels
3539         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3540 }
3541
3542 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3543 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3544
3545 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3546 {
3547         const char *data;
3548         char key[128], value[4096];
3549         float v[3];
3550         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3551         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3552         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3553         if (!l->filelen)
3554                 return;
3555         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3556         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3557         data = loadmodel->brush.entities;
3558         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3559         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3560         {
3561                 while (1)
3562                 {
3563                         if (!COM_ParseToken(&data, false))
3564                                 break; // error
3565                         if (com_token[0] == '}')
3566                                 break; // end of worldspawn
3567                         if (com_token[0] == '_')
3568                                 strcpy(key, com_token + 1);
3569                         else
3570                                 strcpy(key, com_token);
3571                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3572                                 key[strlen(key)-1] = 0;
3573                         if (!COM_ParseToken(&data, false))
3574                                 break; // error
3575                         strcpy(value, com_token);
3576                         if (!strcmp("gridsize", key))
3577                         {
3578                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3579                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3580                         }
3581                 }
3582         }
3583 }
3584
3585 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3586 {
3587         q3dtexture_t *in;
3588         q3mtexture_t *out;
3589         int i, count;
3590         int j, c;
3591         fssearch_t *search;
3592         char *f;
3593         const char *text;
3594         int flags;
3595         char shadername[Q3PATHLENGTH];
3596         char sky[Q3PATHLENGTH];
3597
3598         in = (void *)(mod_base + l->fileofs);
3599         if (l->filelen % sizeof(*in))
3600                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3601         count = l->filelen / sizeof(*in);
3602         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3603
3604         loadmodel->brushq3.data_textures = out;
3605         loadmodel->brushq3.num_textures = count;
3606
3607         for (i = 0;i < count;i++, in++, out++)
3608         {
3609                 out->number = i;
3610                 strlcpy (out->name, in->name, sizeof (out->name));
3611                 out->surfaceflags = LittleLong(in->surfaceflags);
3612                 out->nativecontents = LittleLong(in->contents);
3613                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3614                 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true, true);
3615                 out->surfaceparms = -1;
3616         }
3617
3618         // do a quick parse of shader files to get surfaceparms
3619         if ((search = FS_Search("scripts/*.shader", true, false)))
3620         {
3621                 for (i = 0;i < search->numfilenames;i++)
3622                 {
3623                         if ((f = FS_LoadFile(search->filenames[i], tempmempool, false)))
3624                         {
3625                                 text = f;
3626                                 while (COM_ParseToken(&text, false))
3627                                 {
3628                                         strlcpy (shadername, com_token, sizeof (shadername));
3629                                         flags = 0;
3630                                         sky[0] = 0;
3631                                         if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
3632                                         {
3633                                                 while (COM_ParseToken(&text, false))
3634                                                 {
3635                                                         if (!strcasecmp(com_token, "}"))
3636                                                                 break;
3637                                                         else if (!strcasecmp(com_token, "{"))
3638                                                         {
3639                                                                 while (COM_ParseToken(&text, false))
3640                                                                 {
3641                                                                         if (!strcasecmp(com_token, "}"))
3642                                                                                 break;
3643                                                                 }
3644                                                         }
3645                                                         else if (!strcasecmp(com_token, "surfaceparm"))
3646                                                         {
3647                                                                 if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3648                                                                 {
3649                                                                         if (!strcasecmp(com_token, "alphashadow"))
3650                                                                                 flags |= Q3SURFACEPARM_ALPHASHADOW;
3651                                                                         else if (!strcasecmp(com_token, "areaportal"))
3652                                                                                 flags |= Q3SURFACEPARM_AREAPORTAL;
3653                                                                         else if (!strcasecmp(com_token, "clusterportal"))
3654                                                                                 flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3655                                                                         else if (!strcasecmp(com_token, "detail"))
3656                                                                                 flags |= Q3SURFACEPARM_DETAIL;
3657                                                                         else if (!strcasecmp(com_token, "donotenter"))
3658                                                                                 flags |= Q3SURFACEPARM_DONOTENTER;
3659                                                                         else if (!strcasecmp(com_token, "fog"))
3660                                                                                 flags |= Q3SURFACEPARM_FOG;
3661                                                                         else if (!strcasecmp(com_token, "lava"))
3662                                                                                 flags |= Q3SURFACEPARM_LAVA;
3663                                                                         else if (!strcasecmp(com_token, "lightfilter"))
3664                                                                                 flags |= Q3SURFACEPARM_LIGHTFILTER;
3665                                                                         else if (!strcasecmp(com_token, "metalsteps"))
3666                                                                                 flags |= Q3SURFACEPARM_METALSTEPS;
3667                                                                         else if (!strcasecmp(com_token, "nodamage"))
3668                                                                                 flags |= Q3SURFACEPARM_NODAMAGE;
3669                                                                         else if (!strcasecmp(com_token, "nodlight"))
3670                                                                                 flags |= Q3SURFACEPARM_NODLIGHT;
3671                                                                         else if (!strcasecmp(com_token, "nodraw"))
3672                                                                                 flags |= Q3SURFACEPARM_NODRAW;
3673                                                                         else if (!strcasecmp(com_token, "nodrop"))
3674                                                                                 flags |= Q3SURFACEPARM_NODROP;
3675                                                                         else if (!strcasecmp(com_token, "noimpact"))
3676                                                                                 flags |= Q3SURFACEPARM_NOIMPACT;
3677                                                                         else if (!strcasecmp(com_token, "nolightmap"))
3678                                                                                 flags |= Q3SURFACEPARM_NOLIGHTMAP;
3679                                                                         else if (!strcasecmp(com_token, "nomarks"))
3680                                                                                 flags |= Q3SURFACEPARM_NOMARKS;
3681                                                                         else if (!strcasecmp(com_token, "nomipmaps"))
3682                                                                                 flags |= Q3SURFACEPARM_NOMIPMAPS;
3683                                                                         else if (!strcasecmp(com_token, "nonsolid"))
3684                                                                                 flags |= Q3SURFACEPARM_NONSOLID;
3685                                                                         else if (!strcasecmp(com_token, "origin"))
3686                                                                                 flags |= Q3SURFACEPARM_ORIGIN;
3687                                                                         else if (!strcasecmp(com_token, "playerclip"))
3688                                                                                 flags |= Q3SURFACEPARM_PLAYERCLIP;
3689                                                                         else if (!strcasecmp(com_token, "sky"))
3690                                                                                 flags |= Q3SURFACEPARM_SKY;
3691                                                                         else if (!strcasecmp(com_token, "slick"))
3692                                                                                 flags |= Q3SURFACEPARM_SLICK;
3693                                                                         else if (!strcasecmp(com_token, "slime"))
3694                                                                                 flags |= Q3SURFACEPARM_SLIME;
3695                                                                         else if (!strcasecmp(com_token, "structural"))
3696                                                                                 flags |= Q3SURFACEPARM_STRUCTURAL;
3697                                                                         else if (!strcasecmp(com_token, "trans"))
3698                                                                                 flags |= Q3SURFACEPARM_TRANS;
3699                                                                         else if (!strcasecmp(com_token, "water"))
3700                                                                                 flags |= Q3SURFACEPARM_WATER;
3701                                                                         else
3702                                                                                 Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
3703                                                                         if (!COM_ParseToken(&text, true) || strcasecmp(com_token, "\n"))
3704                                                                         {
3705                                                                                 Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
3706                                                                                 goto parseerror;
3707                                                                         }
3708                                                                 }
3709                                                                 else
3710                                                                 {
3711                                                                         Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
3712                                                                         goto parseerror;
3713                                                                 }
3714                                                         }
3715                                                         else if (!strcasecmp(com_token, "sky"))
3716                                                         {
3717                                                                 if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3718                                                                         if (strlen(com_token) < sizeof(sky))
3719                                                                                 strcpy(sky, com_token);
3720                                                         }
3721                                                         else if (!strcasecmp(com_token, "skyparms"))
3722                                                         {
3723                                                                 if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3724                                                                 {
3725                                                                         if (strlen(com_token) < sizeof(sky) && !atoi(com_token) && strcasecmp(com_token, "-"))
3726                                                                                 strcpy(sky, com_token);
3727                                                                         if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3728                                                                                 COM_ParseToken(&text, true);
3729                                                                 }
3730                                                         }
3731                                                         else
3732                                                         {
3733                                                                 // look for linebreak or }
3734                                                                 while(COM_ParseToken(&text, true) && strcasecmp(com_token, "\n") && strcasecmp(com_token, "}"));
3735                                                                 // break out to top level if it was }
3736                                                                 if (!strcasecmp(com_token, "}"))
3737                                                                         break;
3738                                                         }
3739                                                 }
3740                                                 // add shader to list (shadername and flags)
3741                                                 // actually here we just poke into the texture settings
3742                                                 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3743                                                 {
3744                                                         if (!strcasecmp(out->name, shadername))
3745                                                         {
3746                                                                 out->surfaceparms = flags;
3747                                                                 if ((flags & Q3SURFACEPARM_SKY) && sky[0])
3748                                                                 {
3749                                                                         // quake3 seems to append a _ to the skybox name, so this must do so as well
3750                                                                         snprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
3751                                                                 }
3752                                                         }
3753                                                 }
3754                                         }
3755                                         else
3756                                         {
3757                                                 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3758                                                 goto parseerror;
3759                                         }
3760                                 }
3761 parseerror:
3762                                 Mem_Free(f);
3763                         }
3764                 }
3765         }
3766
3767         c = 0;
3768         for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3769         {
3770                 if (out->surfaceparms == -1)
3771                 {
3772                         c++;
3773                         Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3774                         out->surfaceparms = 0;
3775                         // these are defaults
3776                         if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3777                          || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3778                                 out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3779                         if (!strncmp(out->name, "textures/skies/", 15))
3780                                 out->surfaceparms |= Q3SURFACEPARM_SKY;
3781                         if (R_TextureHasAlpha(out->skin.base))
3782                                 out->surfaceparms |= Q3SURFACEPARM_TRANS;
3783                 }
3784         }
3785         Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3786 }
3787
3788 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3789 {
3790         q3dplane_t *in;
3791         mplane_t *out;
3792         int i, count;
3793
3794         in = (void *)(mod_base + l->fileofs);
3795         if (l->filelen % sizeof(*in))
3796                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3797         count = l->filelen / sizeof(*in);
3798         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3799
3800         loadmodel->brushq3.data_planes = out;
3801         loadmodel->brushq3.num_planes = count;
3802
3803         for (i = 0;i < count;i++, in++, out++)
3804         {
3805                 out->normal[0] = LittleLong(in->normal[0]);
3806                 out->normal[1] = LittleLong(in->normal[1]);
3807                 out->normal[2] = LittleLong(in->normal[2]);
3808                 out->dist = LittleLong(in->dist);
3809                 PlaneClassify(out);
3810         }
3811 }
3812
3813 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3814 {
3815         q3dbrushside_t *in;
3816         q3mbrushside_t *out;
3817         int i, n, count;
3818
3819         in = (void *)(mod_base + l->fileofs);
3820         if (l->filelen % sizeof(*in))
3821                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3822         count = l->filelen / sizeof(*in);
3823         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3824
3825         loadmodel->brushq3.data_brushsides = out;
3826         loadmodel->brushq3.num_brushsides = count;
3827
3828         for (i = 0;i < count;i++, in++, out++)
3829         {
3830                 n = LittleLong(in->planeindex);
3831                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3832                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3833                 out->plane = loadmodel->brushq3.data_planes + n;
3834                 n = LittleLong(in->textureindex);
3835                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3836                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3837                 out->texture = loadmodel->brushq3.data_textures + n;
3838         }
3839 }
3840
3841 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3842 {
3843         q3dbrush_t *in;
3844         q3mbrush_t *out;
3845         int i, j, n, c, count, maxplanes;
3846         mplane_t *planes;
3847
3848         in = (void *)(mod_base + l->fileofs);
3849         if (l->filelen % sizeof(*in))
3850                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3851         count = l->filelen / sizeof(*in);
3852         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3853
3854         loadmodel->brushq3.data_brushes = out;
3855         loadmodel->brushq3.num_brushes = count;
3856
3857         maxplanes = 0;
3858         planes = NULL;
3859
3860         for (i = 0;i < count;i++, in++, out++)
3861         {
3862                 n = LittleLong(in->firstbrushside);
3863                 c = LittleLong(in->numbrushsides);
3864                 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3865                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3866                 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3867                 out->numbrushsides = c;
3868                 n = LittleLong(in->textureindex);
3869                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3870                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3871                 out->texture = loadmodel->brushq3.data_textures + n;
3872
3873                 // make a list of mplane_t structs to construct a colbrush from
3874                 if (maxplanes < out->numbrushsides)
3875                 {
3876                         maxplanes = out->numbrushsides;
3877                         if (planes)
3878                                 Mem_Free(planes);
3879                         planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3880                 }
3881                 for (j = 0;j < out->numbrushsides;j++)
3882                 {
3883                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3884                         planes[j].dist = out->firstbrushside[j].plane->dist;
3885                 }
3886                 // make the colbrush from the planes
3887                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3888         }
3889         if (planes)
3890                 Mem_Free(planes);
3891 }
3892
3893 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3894 {
3895         q3deffect_t *in;
3896         q3meffect_t *out;
3897         int i, n, count;
3898
3899         in = (void *)(mod_base + l->fileofs);
3900         if (l->filelen % sizeof(*in))
3901                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3902         count = l->filelen / sizeof(*in);
3903         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3904
3905         loadmodel->brushq3.data_effects = out;
3906         loadmodel->brushq3.num_effects = count;
3907
3908         for (i = 0;i < count;i++, in++, out++)
3909         {
3910                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3911                 n = LittleLong(in->brushindex);
3912                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3913                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3914                 out->brush = loadmodel->brushq3.data_brushes + n;
3915                 out->unknown = LittleLong(in->unknown);
3916         }
3917 }
3918
3919 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3920 {
3921         q3dvertex_t *in;
3922         int i, count;
3923
3924         in = (void *)(mod_base + l->fileofs);
3925         if (l->filelen % sizeof(*in))
3926                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3927         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3928         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3929         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3930         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3931         loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3932         loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3933         loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3934         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3935
3936         for (i = 0;i < count;i++, in++)
3937         {
3938                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3939                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3940                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3941                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3942                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3943                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3944                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3945                 // svector/tvector are calculated later in face loading
3946                 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3947                 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3948                 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3949                 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3950                 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3951                 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3952                 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3953                 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3954                 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3955                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3956                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3957                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3958                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3959         }
3960 }
3961
3962 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3963 {
3964         int *in;
3965         int *out;
3966         int i, count;
3967
3968         in = (void *)(mod_base + l->fileofs);
3969         if (l->filelen % sizeof(int[3]))
3970                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3971         count = l->filelen / sizeof(*in);
3972         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3973
3974         loadmodel->brushq3.num_triangles = count / 3;
3975         loadmodel->brushq3.data_element3i = out;
3976         loadmodel->brushq3.data_neighbor3i = out + count;
3977
3978         for (i = 0;i < count;i++, in++, out++)
3979         {
3980                 *out = LittleLong(*in);
3981                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3982                 {
3983                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3984                         *out = 0;
3985                 }
3986         }
3987 }
3988
3989 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3990 {
3991         q3dlightmap_t *in;
3992         rtexture_t **out;
3993         int i, count;
3994
3995         if (!l->filelen)
3996                 return;
3997         in = (void *)(mod_base + l->fileofs);
3998         if (l->filelen % sizeof(*in))
3999                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
4000         count = l->filelen / sizeof(*in);
4001         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4002
4003         loadmodel->brushq3.data_lightmaps = out;
4004         loadmodel->brushq3.num_lightmaps = count;
4005
4006         for (i = 0;i < count;i++, in++, out++)
4007                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4008 }
4009
4010 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4011 {
4012         q3dface_t *in;
4013         q3msurface_t *out;
4014         int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
4015         //int *originalelement3i;
4016         //int *originalneighbor3i;
4017         float *originalvertex3f;
4018         //float *originalsvector3f;
4019         //float *originaltvector3f;
4020         //float *originalnormal3f;
4021         float *originalcolor4f;
4022         float *originaltexcoordtexture2f;
4023         float *originaltexcoordlightmap2f;
4024         float *v;
4025
4026         in = (void *)(mod_base + l->fileofs);
4027         if (l->filelen % sizeof(*in))
4028                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4029         count = l->filelen / sizeof(*in);
4030         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4031
4032         loadmodel->brushq3.data_faces = out;
4033         loadmodel->brushq3.num_faces = count;
4034
4035         for (i = 0;i < count;i++, in++, out++)
4036         {
4037                 // check face type first
4038                 out->type = LittleLong(in->type);
4039                 if (out->type != Q3FACETYPE_POLYGON
4040                  && out->type != Q3FACETYPE_PATCH
4041                  && out->type != Q3FACETYPE_MESH
4042                  && out->type != Q3FACETYPE_FLARE)
4043                 {
4044                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
4045                         out->num_vertices = 0;
4046                         out->num_triangles = 0;
4047                         out->type = 0; // error
4048                         continue;
4049                 }
4050
4051                 n = LittleLong(in->textureindex);
4052                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
4053                 {
4054                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
4055                         out->num_vertices = 0;
4056                         out->num_triangles = 0;
4057                         out->type = 0; // error
4058                         continue;
4059                         n = 0;
4060                 }
4061                 out->texture = loadmodel->brushq3.data_textures + n;
4062                 n = LittleLong(in->effectindex);
4063                 if (n < -1 || n >= loadmodel->brushq3.num_effects)
4064                 {
4065                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4066                         n = -1;
4067                 }
4068                 if (n == -1)
4069                         out->effect = NULL;
4070                 else
4071                         out->effect = loadmodel->brushq3.data_effects + n;
4072                 n = LittleLong(in->lightmapindex);
4073                 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
4074                 {
4075                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4076                         n = -1;
4077                 }
4078                 if (n == -1)
4079                         out->lightmaptexture = NULL;
4080                 else
4081                         out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4082
4083                 out->firstvertex = LittleLong(in->firstvertex);
4084                 out->num_vertices = LittleLong(in->numvertices);
4085                 out->firstelement = LittleLong(in->firstelement);
4086                 out->num_triangles = LittleLong(in->numelements) / 3;
4087                 if (out->num_triangles * 3 != LittleLong(in->numelements))
4088                 {
4089                         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));
4090                         out->num_vertices = 0;
4091                         out->num_triangles = 0;
4092                         out->type = 0; // error
4093                         continue;
4094                 }
4095                 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
4096                 {
4097                         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);
4098                         out->num_vertices = 0;
4099                         out->num_triangles = 0;
4100                         out->type = 0; // error
4101                         continue;
4102                 }
4103                 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
4104                 {
4105                         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);
4106                         out->num_vertices = 0;
4107                         out->num_triangles = 0;
4108                         out->type = 0; // error
4109                         continue;
4110                 }
4111                 switch(out->type)
4112                 {
4113                 case Q3FACETYPE_POLYGON:
4114                 case Q3FACETYPE_MESH:
4115                         // no processing necessary
4116                         out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4117                         out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4118                         out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4119                         out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4120                         out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4121                         out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4122                         out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4123                         out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4124                         out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4125                         break;
4126                 case Q3FACETYPE_PATCH:
4127                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4128                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4129                         if (patchsize[0] < 1 || patchsize[1] < 1)
4130                         {
4131                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4132                                 out->num_vertices = 0;
4133                                 out->num_triangles = 0;
4134                                 out->type = 0; // error
4135                                 continue;
4136                         }
4137                         originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4138                         //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4139                         //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4140                         //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4141                         originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4142                         originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4143                         originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4144                         //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
4145                         //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4146                         /*
4147                         originalvertex3f = out->data_vertex3f;
4148                         //originalsvector3f = out->data_svector3f;
4149                         //originaltvector3f = out->data_tvector3f;
4150                         //originalnormal3f = out->data_normal3f;
4151                         originalcolor4f = out->data_color4f;
4152                         originaltexcoordtexture2f = out->data_texcoordtexture2f;
4153                         originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
4154                         //originalelement3i = out->data_element3i;
4155                         //originalneighbor3i = out->data_neighbor3i;
4156                         */
4157                         // convert patch to Q3FACETYPE_MESH
4158                         xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
4159                         ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
4160                         // bound to user settings
4161                         xlevel = bound(r_subdivisions_minlevel.integer, xlevel, r_subdivisions_maxlevel.integer);
4162                         ylevel = bound(r_subdivisions_minlevel.integer, ylevel, r_subdivisions_maxlevel.integer);
4163                         // bound to sanity settings
4164                         xlevel = bound(0, xlevel, 10);
4165                         ylevel = bound(0, ylevel, 10);
4166                         // bound to user limit on vertices
4167                         while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4168                         {
4169                                 if (xlevel > ylevel)
4170                                         xlevel--;
4171                                 else
4172                                         ylevel--;
4173                         }
4174                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
4175                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
4176                         finalvertices = finalwidth * finalheight;
4177                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4178                         out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
4179                         out->data_svector3f = out->data_vertex3f + finalvertices * 3;
4180                         out->data_tvector3f = out->data_svector3f + finalvertices * 3;
4181                         out->data_normal3f = out->data_tvector3f + finalvertices * 3;
4182                         out->data_color4f = out->data_normal3f + finalvertices * 3;
4183                         out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
4184                         out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
4185                         out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
4186                         out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
4187                         out->type = Q3FACETYPE_MESH;
4188                         out->firstvertex = -1;
4189                         out->num_vertices = finalvertices;
4190                         out->firstelement = -1;
4191                         out->num_triangles = finaltriangles;
4192                         // generate geometry
4193                         // (note: normals are skipped because they get recalculated)
4194                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
4195                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
4196                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
4197                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
4198                         // generate elements
4199                         e = out->data_element3i;
4200                         for (y = 0;y < finalheight - 1;y++)
4201                         {
4202                                 row0 = (y + 0) * finalwidth;
4203                                 row1 = (y + 1) * finalwidth;
4204                                 for (x = 0;x < finalwidth - 1;x++)
4205                                 {
4206                                         *e++ = row0;
4207                                         *e++ = row1;
4208                                         *e++ = row0 + 1;
4209                                         *e++ = row1;
4210                                         *e++ = row1 + 1;
4211                                         *e++ = row0 + 1;
4212                                         row0++;
4213                                         row1++;
4214                                 }
4215                         }
4216                         out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
4217                         if (developer.integer)
4218                         {
4219                                 if (out->num_triangles < finaltriangles)
4220                                         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);
4221                                 else
4222                                         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);
4223                         }
4224                         // q3map does not put in collision brushes for curves... ugh
4225                         // build the lower quality collision geometry
4226                         out->collisions = true;
4227                         xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
4228                         ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
4229                         // bound to user settings
4230                         xlevel = bound(r_subdivisions_collision_minlevel.integer, xlevel, r_subdivisions_collision_maxlevel.integer);
4231                         ylevel = bound(r_subdivisions_collision_minlevel.integer, ylevel, r_subdivisions_collision_maxlevel.integer);
4232                         // bound to sanity settings
4233                         xlevel = bound(0, xlevel, 10);
4234                         ylevel = bound(0, ylevel, 10);
4235                         // bound to user limit on vertices
4236                         while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
4237                         {
4238                                 if (xlevel > ylevel)
4239                                         xlevel--;
4240                                 else
4241                                         ylevel--;
4242                         }
4243                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
4244                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
4245                         finalvertices = finalwidth * finalheight;
4246                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4247                         out->data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
4248                         out->data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
4249                         out->num_collisionvertices = finalvertices;
4250                         out->num_collisiontriangles = finaltriangles;
4251                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_collisionvertex3f);
4252                         // generate elements
4253                         e = out->data_collisionelement3i;
4254                         for (y = 0;y < finalheight - 1;y++)
4255                         {
4256                                 row0 = (y + 0) * finalwidth;
4257                                 row1 = (y + 1) * finalwidth;
4258                                 for (x = 0;x < finalwidth - 1;x++)
4259                                 {
4260                                         *e++ = row0;
4261                                         *e++ = row1;
4262                                         *e++ = row0 + 1;
4263                                         *e++ = row1;
4264                                         *e++ = row1 + 1;
4265                                         *e++ = row0 + 1;
4266                                         row0++;
4267                                         row1++;
4268                                 }
4269                         }
4270                         out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
4271                         if (developer.integer)
4272                         {
4273                                 if (out->num_collisiontriangles < finaltriangles)
4274                                         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);
4275                                 else
4276                                         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);
4277                         }
4278                         break;
4279                 case Q3FACETYPE_FLARE:
4280                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4281                         // don't render it
4282                         out->num_vertices = 0;
4283                         out->num_triangles = 0;
4284                         out->type = 0;
4285                         break;
4286                 }
4287                 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4288                         if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4289                                 invalidelements++;
4290                 if (invalidelements)
4291                 {
4292                         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);
4293                         for (j = 0;j < out->num_triangles * 3;j++)
4294                         {
4295                                 Con_Printf(" %i", out->data_element3i[j]);
4296                                 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4297                                         out->data_element3i[j] = 0;
4298                         }
4299                         Con_Print("\n");
4300                 }
4301                 // for shadow volumes
4302                 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
4303                 // for per pixel lighting
4304                 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);
4305                 // calculate a bounding box
4306                 VectorClear(out->mins);
4307                 VectorClear(out->maxs);
4308                 if (out->num_vertices)
4309                 {
4310                         VectorCopy(out->data_vertex3f, out->mins);
4311                         VectorCopy(out->data_vertex3f, out->maxs);
4312                         for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
4313                         {
4314                                 out->mins[0] = min(out->mins[0], v[0]);
4315                                 out->maxs[0] = max(out->maxs[0], v[0]);
4316                                 out->mins[1] = min(out->mins[1], v[1]);
4317                                 out->maxs[1] = max(out->maxs[1], v[1]);
4318                                 out->mins[2] = min(out->mins[2], v[2]);
4319                                 out->maxs[2] = max(out->maxs[2], v[2]);
4320                         }
4321                         out->mins[0] -= 1.0f;
4322                         out->mins[1] -= 1.0f;
4323                         out->mins[2] -= 1.0f;
4324                         out->maxs[0] += 1.0f;
4325                         out->maxs[1] += 1.0f;
4326                         out->maxs[2] += 1.0f;
4327                 }
4328         }
4329
4330         // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
4331         /*
4332         {
4333         int totalverts, totaltris;
4334         int originalnum_vertices;
4335         float *originaldata_vertex3f;
4336         float *originaldata_texcoordtexture2f;
4337         float *originaldata_texcoordlightmap2f;
4338         float *originaldata_svector3f;
4339         float *originaldata_tvector3f;
4340         float *originaldata_normal3f;
4341         float *originaldata_color4f;
4342         int originalnum_triangles;
4343         int *originaldata_element3i;
4344         int *originaldata_neighbor3i;
4345
4346         totalverts = 0;
4347         totaltris = 0;
4348         for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4349         {
4350                 if (!out->type)
4351                         continue;
4352                 totalverts += out->num_vertices;
4353                 totaltris += out->num_triangles;
4354         }
4355
4356         originalnum_vertices = loadmodel->brushq3.num_vertices;
4357         originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
4358         originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
4359         originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
4360         originaldata_svector3f = loadmodel->brushq3.data_svector3f;
4361         originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
4362         originaldata_normal3f = loadmodel->brushq3.data_normal3f;
4363         originaldata_color4f = loadmodel->brushq3.data_color4f;
4364         originalnum_triangles = loadmodel->brushq3.num_triangles;
4365         originaldata_element3i = loadmodel->brushq3.data_element3i;
4366         originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
4367         loadmodel->brushq3.num_vertices = totalverts;
4368         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
4369         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
4370         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
4371         loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
4372         loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
4373         loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
4374         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
4375         loadmodel->brushq3.num_triangles = totaltris;
4376         loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
4377         loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
4378         totalverts = 0;
4379         totaltris = 0;
4380         for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4381         {
4382                 if (!out->type)
4383                         continue;
4384                 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
4385                 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
4386                 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
4387                 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
4388                 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
4389                 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
4390                 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
4391                 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
4392                 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
4393                 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
4394                 if (out->firstvertex == -1)
4395                         Mem_Free(out->data_vertex3f);
4396                 if (out->firstelement == -1)
4397                         Mem_Free(out->data_element3i);
4398                 out->firstvertex = totalverts;
4399                 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4400                 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4401                 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4402                 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4403                 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4404                 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4405                 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4406                 out->firstelement = totaltris * 3;
4407                 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4408                 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4409                 //for (j = 0;j < out->numtriangles * 3;j++)
4410                 //      out->data_element3i[j] += totalverts - out->firstvertex;
4411                 totalverts += out->num_vertices;
4412                 totaltris += out->num_triangles;
4413         }
4414         Mem_Free(originaldata_vertex3f);
4415         Mem_Free(originaldata_element3i);
4416         }
4417         */
4418 }
4419
4420 static void Mod_Q3BSP_LoadModels(lump_t *l)
4421 {
4422         q3dmodel_t *in;
4423         q3mmodel_t *out;
4424         int i, j, n, c, count;
4425
4426         in = (void *)(mod_base + l->fileofs);
4427         if (l->filelen % sizeof(*in))
4428                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4429         count = l->filelen / sizeof(*in);
4430         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4431
4432         loadmodel->brushq3.data_models = out;
4433         loadmodel->brushq3.num_models = count;
4434
4435         for (i = 0;i < count;i++, in++, out++)
4436         {
4437                 for (j = 0;j < 3;j++)
4438                 {
4439                         out->mins[j] = LittleFloat(in->mins[j]);
4440                         out->maxs[j] = LittleFloat(in->maxs[j]);
4441                 }
4442                 n = LittleLong(in->firstface);
4443                 c = LittleLong(in->numfaces);
4444                 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4445                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4446                 out->firstface = loadmodel->brushq3.data_faces + n;
4447                 out->numfaces = c;
4448                 n = LittleLong(in->firstbrush);
4449                 c = LittleLong(in->numbrushes);
4450                 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4451                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4452                 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4453                 out->numbrushes = c;
4454         }
4455 }
4456
4457 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4458 {
4459         int *in;
4460         q3mbrush_t **out;
4461         int i, n, count;
4462
4463         in = (void *)(mod_base + l->fileofs);
4464         if (l->filelen % sizeof(*in))
4465                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4466         count = l->filelen / sizeof(*in);
4467         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4468
4469         loadmodel->brushq3.data_leafbrushes = out;
4470         loadmodel->brushq3.num_leafbrushes = count;
4471
4472         for (i = 0;i < count;i++, in++, out++)
4473         {
4474                 n = LittleLong(*in);
4475                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4476                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4477                 *out = loadmodel->brushq3.data_brushes + n;
4478         }
4479 }
4480
4481 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4482 {
4483         int *in;
4484         q3msurface_t **out;
4485         int i, n, count;
4486
4487         in = (void *)(mod_base + l->fileofs);
4488         if (l->filelen % sizeof(*in))
4489                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4490         count = l->filelen / sizeof(*in);
4491         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4492
4493         loadmodel->brushq3.data_leaffaces = out;
4494         loadmodel->brushq3.num_leaffaces = count;
4495
4496         for (i = 0;i < count;i++, in++, out++)
4497         {
4498                 n = LittleLong(*in);
4499                 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4500                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4501                 *out = loadmodel->brushq3.data_faces + n;
4502         }
4503 }
4504
4505 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4506 {
4507         q3dleaf_t *in;
4508         q3mleaf_t *out;
4509         int i, j, n, c, count;
4510
4511         in = (void *)(mod_base + l->fileofs);
4512         if (l->filelen % sizeof(*in))
4513                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4514         count = l->filelen / sizeof(*in);
4515         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4516
4517         loadmodel->brushq3.data_leafs = out;
4518         loadmodel->brushq3.num_leafs = count;
4519
4520         for (i = 0;i < count;i++, in++, out++)
4521         {
4522                 out->parent = NULL;
4523                 out->plane = NULL;
4524                 out->clusterindex = LittleLong(in->clusterindex);
4525                 out->areaindex = LittleLong(in->areaindex);
4526                 for (j = 0;j < 3;j++)
4527                 {
4528                         // yes the mins/maxs are ints
4529                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4530                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4531                 }
4532                 n = LittleLong(in->firstleafface);
4533                 c = LittleLong(in->numleaffaces);
4534                 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4535                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4536                 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4537                 out->numleaffaces = c;
4538                 n = LittleLong(in->firstleafbrush);
4539                 c = LittleLong(in->numleafbrushes);
4540                 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4541                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4542                 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4543                 out->numleafbrushes = c;
4544         }
4545 }
4546
4547 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4548 {
4549         if (node->parent)
4550                 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4551         node->parent = parent;
4552         if (node->plane)
4553         {
4554                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4555                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4556         }
4557 }
4558
4559 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4560 {
4561         q3dnode_t *in;
4562         q3mnode_t *out;
4563         int i, j, n, count;
4564
4565         in = (void *)(mod_base + l->fileofs);
4566         if (l->filelen % sizeof(*in))
4567                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4568         count = l->filelen / sizeof(*in);
4569         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4570
4571         loadmodel->brushq3.data_nodes = out;
4572         loadmodel->brushq3.num_nodes = count;
4573
4574         for (i = 0;i < count;i++, in++, out++)
4575         {
4576                 out->parent = NULL;
4577                 n = LittleLong(in->planeindex);
4578                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4579                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4580                 out->plane = loadmodel->brushq3.data_planes + n;
4581                 for (j = 0;j < 2;j++)
4582                 {
4583                         n = LittleLong(in->childrenindex[j]);
4584                         if (n >= 0)
4585                         {
4586                                 if (n >= loadmodel->brushq3.num_nodes)
4587                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4588                                 out->children[j] = loadmodel->brushq3.data_nodes + n;
4589                         }
4590                         else
4591                         {
4592                                 n = -1 - n;
4593                                 if (n >= loadmodel->brushq3.num_leafs)
4594                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4595                                 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4596                         }
4597                 }
4598                 for (j = 0;j < 3;j++)
4599                 {
4600                         // yes the mins/maxs are ints
4601                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4602                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4603                 }
4604         }
4605
4606         // set the parent pointers
4607         Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4608 }
4609
4610 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4611 {
4612         q3dlightgrid_t *in;
4613         q3dlightgrid_t *out;
4614         int count;
4615
4616         in = (void *)(mod_base + l->fileofs);
4617         if (l->filelen % sizeof(*in))
4618                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4619         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4620         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4621         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4622         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4623         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4624         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4625         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4626         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4627         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4628         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4629         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4630         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4631         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4632         if (l->filelen)
4633         {
4634                 if (l->filelen < count * (int)sizeof(*in))
4635                         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]);
4636                 if (l->filelen != count * (int)sizeof(*in))
4637                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4638         }
4639
4640         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4641         loadmodel->brushq3.data_lightgrid = out;
4642         loadmodel->brushq3.num_lightgrid = count;
4643
4644         // no swapping or validation necessary
4645         if (l->filelen)
4646                 memcpy(out, in, count * (int)sizeof(*out));
4647         else
4648         {
4649                 // no data, fill with white
4650                 int i;
4651                 for (i = 0;i < count;i++)
4652                 {
4653                         out[i].ambientrgb[0] = 128;
4654                         out[i].ambientrgb[1] = 128;
4655                         out[i].ambientrgb[2] = 128;
4656                         out[i].diffusergb[0] = 0;
4657                         out[i].diffusergb[1] = 0;
4658                         out[i].diffusergb[2] = 0;
4659                         out[i].diffusepitch = 0;
4660                         out[i].diffuseyaw = 0;
4661                 }
4662         }
4663
4664         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]);
4665         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]);
4666 }
4667
4668 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4669 {
4670         q3dpvs_t *in;
4671         int totalchains;
4672
4673         if (l->filelen == 0)
4674         {
4675                 int i;
4676                 // unvised maps often have cluster indices even without pvs, so check
4677                 // leafs to find real number of clusters
4678                 loadmodel->brush.num_pvsclusters = 1;
4679                 for (i = 0;i < loadmodel->brushq3.num_leafs;i++)
4680                         loadmodel->brush.num_pvsclusters = min(loadmodel->brush.num_pvsclusters, loadmodel->brushq3.data_leafs[i].clusterindex + 1);
4681
4682                 // create clusters
4683                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
4684                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4685                 loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4686                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
4687                 return;
4688         }
4689
4690         in = (void *)(mod_base + l->fileofs);
4691         if (l->filelen < 9)
4692                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4693
4694         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
4695         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
4696         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
4697                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
4698         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4699         if (l->filelen < totalchains + (int)sizeof(*in))
4700                 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);
4701
4702         loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4703         memcpy(loadmodel->brush.data_pvsclusters, (qbyte *)(in + 1), totalchains);
4704 }
4705
4706 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4707 {
4708         // FIXME: finish this code
4709         VectorCopy(in, out);
4710 }
4711
4712 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4713 {
4714         int i, j, k, index[3];
4715         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4716         q3dlightgrid_t *a, *s;
4717         // FIXME: write this
4718         if (!model->brushq3.num_lightgrid)
4719         {
4720                 ambientcolor[0] = 1;
4721                 ambientcolor[1] = 1;
4722                 ambientcolor[2] = 1;
4723                 return;
4724         }
4725         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4726         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4727         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4728         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4729         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4730         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4731         index[0] = (int)floor(transformed[0]);
4732         index[1] = (int)floor(transformed[1]);
4733         index[2] = (int)floor(transformed[2]);
4734         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4735         // now lerp the values
4736         VectorClear(diffusenormal);
4737         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4738         for (k = 0;k < 2;k++)
4739         {
4740                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4741                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4742                         continue;
4743                 for (j = 0;j < 2;j++)
4744                 {
4745                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4746                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4747                                 continue;
4748                         for (i = 0;i < 2;i++)
4749                         {
4750                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4751                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4752                                         continue;
4753                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4754                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4755                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4756                                 pitch = s->diffusepitch * M_PI / 128;
4757                                 yaw = s->diffuseyaw * M_PI / 128;
4758                                 sinpitch = sin(pitch);
4759                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4760                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4761                                 diffusenormal[2] += blend * (cos(pitch));
4762                                 //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)));
4763                         }
4764                 }
4765         }
4766         VectorNormalize(diffusenormal);
4767         //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]);
4768 }
4769
4770 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t point, int markframe)
4771 {
4772         int i;
4773         q3mleaf_t *leaf;
4774         colbrushf_t *brush;
4775         // find which leaf the point is in
4776         while (node->plane)
4777                 node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
4778         // point trace the brushes
4779         leaf = (q3mleaf_t *)node;
4780         for (i = 0;i < leaf->numleafbrushes;i++)
4781         {
4782                 brush = leaf->firstleafbrush[i]->colbrushf;
4783                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
4784                 {
4785                         brush->markframe = markframe;
4786                         Collision_TracePointBrushFloat(trace, point, leaf->firstleafbrush[i]->colbrushf);
4787                 }
4788         }
4789         // can't do point traces on curves (they have no thickness)
4790 }
4791
4792 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)
4793 {
4794         int i, startside, endside;
4795         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
4796         q3mleaf_t *leaf;
4797         q3msurface_t *face;
4798         colbrushf_t *brush;
4799         if (startfrac > trace->realfraction)
4800                 return;
4801         // note: all line fragments past first impact fraction are ignored
4802         if (VectorCompare(start, end))
4803         {
4804                 // find which leaf the point is in
4805                 while (node->plane)
4806                         node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist];
4807         }
4808         else
4809         {
4810                 // find which nodes the line is in and recurse for them
4811                 while (node->plane)
4812                 {
4813                         // recurse down node sides
4814                         dist1 = PlaneDiff(start, node->plane);
4815                         dist2 = PlaneDiff(end, node->plane);
4816                         startside = dist1 < 0;
4817                         endside = dist2 < 0;
4818                         if (startside == endside)
4819                         {
4820                                 // most of the time the line fragment is on one side of the plane
4821                                 node = node->children[startside];
4822                         }
4823                         else
4824                         {
4825                                 // line crosses node plane, split the line
4826                                 midfrac = dist1 / (dist1 - dist2);
4827                                 VectorLerp(start, midfrac, end, mid);
4828                                 // take the near side first
4829                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4830                                 if (midfrac <= trace->realfraction)
4831                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4832                                 return;
4833                         }
4834                 }
4835         }
4836         // hit a leaf
4837         nodesegmentmins[0] = min(start[0], end[0]);
4838         nodesegmentmins[1] = min(start[1], end[1]);
4839         nodesegmentmins[2] = min(start[2], end[2]);
4840         nodesegmentmaxs[0] = max(start[0], end[0]);
4841         nodesegmentmaxs[1] = max(start[1], end[1]);
4842         nodesegmentmaxs[2] = max(start[2], end[2]);
4843         // line trace the brushes
4844         leaf = (q3mleaf_t *)node;
4845         for (i = 0;i < leaf->numleafbrushes;i++)
4846         {
4847                 brush = leaf->firstleafbrush[i]->colbrushf;
4848                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4849                 {
4850                         brush->markframe = markframe;
4851                         Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4852                         if (startfrac > trace->realfraction)
4853                                 return;
4854                 }
4855         }
4856         // can't do point traces on curves (they have no thickness)
4857         if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
4858         {
4859                 // line trace the curves
4860                 for (i = 0;i < leaf->numleaffaces;i++)
4861                 {
4862                         face = leaf->firstleafface[i];
4863                         if (face->collisions && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4864                         {
4865                                 face->collisionmarkframe = markframe;
4866                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4867                                 if (startfrac > trace->realfraction)
4868                                         return;
4869                         }
4870                 }
4871         }
4872 }
4873
4874 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)
4875 {
4876         int i;
4877         //int sides;
4878         float nodesegmentmins[3], nodesegmentmaxs[3];
4879         q3mleaf_t *leaf;
4880         colbrushf_t *brush;
4881         q3msurface_t *face;
4882         /*
4883                 // find which nodes the line is in and recurse for them
4884                 while (node->plane)
4885                 {
4886                         // recurse down node sides
4887                         int startside, endside;
4888                         float dist1near, dist1far, dist2near, dist2far;
4889                         BoxPlaneCornerDistances(thisbrush_start->mins, thisbrush_start->maxs, node->plane, &dist1near, &dist1far);
4890                         BoxPlaneCornerDistances(thisbrush_end->mins, thisbrush_end->maxs, node->plane, &dist2near, &dist2far);
4891                         startside = dist1near < 0;
4892                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
4893                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
4894                         if (startside == 2 || endside == 2)
4895                         {
4896                                 // brushes cross plane
4897                                 // do not clip anything, just take both sides
4898                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4899                                 node = node->children[1];
4900                                 continue;
4901                         }
4902                         if (startside == 0)
4903                         {
4904                                 if (endside == 0)
4905                                 {
4906                                         node = node->children[0];
4907                                         continue;
4908                                 }
4909                                 else
4910                                 {
4911                                         //midf0 = dist1near / (dist1near - dist2near);
4912                                         //midf1 = dist1far / (dist1far - dist2far);
4913                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4914                                         node = node->children[1];
4915                                         continue;
4916                                 }
4917                         }
4918                         else
4919                         {
4920                                 if (endside == 0)
4921                                 {
4922                                         //midf0 = dist1near / (dist1near - dist2near);
4923                                         //midf1 = dist1far / (dist1far - dist2far);
4924                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4925                                         node = node->children[1];
4926                                         continue;
4927                                 }
4928                                 else
4929                                 {
4930                                         node = node->children[1];
4931                                         continue;
4932                                 }
4933                         }
4934
4935                         if (dist1near <  0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4936                         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;}
4937                         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;}
4938                         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;}
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){}
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){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4945                         if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far <  0){}
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){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4948                         if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){node = node->children[0];continue;}
4949                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){node = node->children[0];continue;}
4950                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){node = node->children[0];continue;}
4951                         {             
4952                                 if (dist2near < 0) // d1n<0 && d2n<0
4953                                 {
4954                                         if (dist2near < 0) // d1n<0 && d2n<0
4955                                         {
4956                                                 if (dist2near < 0) // d1n<0 && d2n<0
4957                                                 {
4958                                                 }
4959                                                 else // d1n<0 && d2n>0
4960                                                 {
4961                                                 }
4962                                         }
4963                                         else // d1n<0 && d2n>0
4964                                         {
4965                                                 if (dist2near < 0) // d1n<0 && d2n<0
4966                                                 {
4967                                                 }
4968                                                 else // d1n<0 && d2n>0
4969                                                 {
4970                                                 }
4971                                         }
4972                                 }
4973                                 else // d1n<0 && d2n>0
4974                                 {
4975                                 }
4976                         }
4977                         else // d1n>0
4978                         {
4979                                 if (dist2near < 0) // d1n>0 && d2n<0
4980                                 {
4981                                 }
4982                                 else // d1n>0 && d2n>0
4983                                 {
4984                                 }
4985                         }
4986                         if (dist1near < 0 == dist1far < 0 == dist2near < 0 == dist2far < 0)
4987                         {
4988                                 node = node->children[startside];
4989                                 continue;
4990                         }
4991                         if (dist1near < dist2near)
4992                         {
4993                                 // out
4994                                 if (dist1near >= 0)
4995                                 {
4996                                         node = node->children[0];
4997                                         continue;
4998                                 }
4999                                 if (dist2far < 0)
5000                                 {
5001                                         node = node->children[1];
5002                                         continue;
5003                                 }
5004                                 // dist1near < 0 && dist2far >= 0
5005                         }
5006                         else
5007                         {
5008                                 // in
5009                         }
5010                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
5011                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
5012                         if (startside == 2 || endside == 2)
5013                         {
5014                                 // brushes cross plane
5015                                 // do not clip anything, just take both sides
5016                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5017                                 node = node->children[1];
5018                         }
5019                         else if (startside == endside)
5020                                 node = node->children[startside];
5021                         else if (startside == 0) // endside = 1 (start infront, end behind)
5022                         {
5023                         }
5024                         else // startside == 1 endside = 0 (start behind, end infront)
5025                         {
5026                         }
5027                         == endside)
5028                         {
5029                                 if (startside < 2)
5030                                         node = node->children[startside];
5031                                 else
5032                                 {
5033                                         // start and end brush cross plane
5034                                 }
5035                         }
5036                         else
5037                         {
5038                         }
5039                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5040                                 node = node->children[1];
5041                         else if (dist1near < 0 && dist1far < 0 && dist2near >= 0 && dist2far >= 0)
5042                         else if (dist1near >= 0 && dist1far >= 0 && dist2near < 0 && dist2far < 0)
5043                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5044                                 node = node->children[0];
5045                         else
5046                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5047                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5048                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5049                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5050                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5051                         {
5052                         }
5053                         else if (dist1near >= 0 && dist1far >= 0)
5054                         {
5055                         }
5056                         else // mixed (lying on plane)
5057                         {
5058                         }
5059                         {
5060                                 if (dist2near < 0 && dist2far < 0)
5061                                 {
5062                                 }
5063                                 else
5064                                         node = node->children[1];
5065                         }
5066                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5067                                 node = node->children[0];
5068                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5069                                 node = node->children[1];
5070                         else
5071                         {
5072                                 // both sides
5073                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5074                                 node = node->children[1];
5075                         }
5076                         sides = dist1near || dist1near < 0 | dist1far < 0 | dist2near < 0 | dist
5077                         startside = dist1 < 0;
5078                         endside = dist2 < 0;
5079                         if (startside == endside)
5080                         {
5081                                 // most of the time the line fragment is on one side of the plane
5082                                 node = node->children[startside];
5083                         }
5084                         else
5085                         {
5086                                 // line crosses node plane, split the line
5087                                 midfrac = dist1 / (dist1 - dist2);
5088                                 VectorLerp(start, midfrac, end, mid);
5089                                 // take the near side first
5090                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5091                                 if (midfrac <= trace->fraction)
5092                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5093                                 return;
5094                         }
5095                 }
5096         */
5097 #if 1
5098         for (;;)
5099         {
5100                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5101                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5102                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5103                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5104                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5105                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5106                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5107                         return;
5108                 if (!node->plane)
5109                         break;
5110                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5111                 node = node->children[1];
5112         }
5113 #elif 0
5114         // FIXME: could be made faster by copying TraceLine code and making it use
5115         // box plane distances...  (variant on the BoxOnPlaneSide code)
5116         for (;;)
5117         {
5118                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5119                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5120                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5121                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5122                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5123                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5124                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5125                         return;
5126                 if (!node->plane)
5127                         break;
5128                 if (mod_q3bsp_debugtracebrush.integer == 2)
5129                 {
5130                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5131                         node = node->children[1];
5132                         continue;
5133                 }
5134                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5135                 {
5136                         // recurse down node sides
5137                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5138                         if (sides == 3)
5139                         {
5140                                 // segment box crosses plane
5141                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5142                                 node = node->children[1];
5143                                 continue;
5144                         }
5145                         // take whichever side the segment box is on
5146                         node = node->children[sides - 1];
5147                         continue;
5148                 }
5149                 else
5150                 {
5151                         // recurse down node sides
5152                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5153                         if (sides == 3)
5154                         {
5155                                 // segment box crosses plane
5156                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5157                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5158                                 if (sides == 3)
5159                                 {
5160                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5161                                         node = node->children[1];
5162                                         continue;
5163                                 }
5164                         }
5165                         // take whichever side the segment box is on
5166                         node = node->children[sides - 1];
5167                         continue;
5168                 }
5169                 return;
5170         }
5171 #else
5172         // FIXME: could be made faster by copying TraceLine code and making it use
5173         // box plane distances...  (variant on the BoxOnPlaneSide code)
5174         for (;;)
5175         {
5176                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5177                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5178                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5179                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5180                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5181                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5182                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5183                         return;
5184                 if (!node->plane)
5185                         break;
5186                 if (mod_q3bsp_debugtracebrush.integer == 2)
5187                 {
5188                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5189                         node = node->children[1];
5190                 }
5191                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5192                 {
5193                         // recurse down node sides
5194                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5195                         if (sides == 3)
5196                         {
5197                                 // segment box crosses plane
5198                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5199                                 node = node->children[1];
5200                         }
5201                         else
5202                         {
5203                                 // take whichever side the segment box is on
5204                                 node = node->children[sides - 1];
5205                         }
5206                 }
5207                 else
5208                 {
5209                         // recurse down node sides
5210                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5211                         if (sides == 3)
5212                         {
5213                                 // segment box crosses plane
5214                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5215                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5216                                 if (sides == 3)
5217                                 {
5218                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5219                                         sides = 2;
5220                                 }
5221                         }
5222                         // take whichever side the segment box is on
5223                         node = node->children[sides - 1];
5224                 }
5225         }
5226 #endif
5227         // hit a leaf
5228         leaf = (q3mleaf_t *)node;
5229         for (i = 0;i < leaf->numleafbrushes;i++)
5230         {
5231                 brush = leaf->firstleafbrush[i]->colbrushf;
5232                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5233                 {
5234                         brush->markframe = markframe;
5235                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
5236                 }
5237         }
5238         if (mod_q3bsp_curves_collisions.integer)
5239         {
5240                 for (i = 0;i < leaf->numleaffaces;i++)
5241                 {
5242                         face = leaf->firstleafface[i];
5243                         if (face->collisions && face->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
5244                         {
5245                                 face->markframe = markframe;
5246                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5247                         }
5248                 }
5249         }
5250 }
5251
5252 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)
5253 {
5254         int i;
5255         float segmentmins[3], segmentmaxs[3];
5256         colbrushf_t *thisbrush_start, *thisbrush_end;
5257         matrix4x4_t startmatrix, endmatrix;
5258         static int markframe = 0;
5259         q3msurface_t *face;
5260         memset(trace, 0, sizeof(*trace));
5261         trace->fraction = 1;
5262         trace->realfraction = 1;
5263         trace->hitsupercontentsmask = hitsupercontentsmask;
5264         Matrix4x4_CreateIdentity(&startmatrix);
5265         Matrix4x4_CreateIdentity(&endmatrix);
5266         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
5267         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
5268         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
5269         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
5270         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
5271         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
5272         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
5273         {
5274                 if (VectorCompare(boxstartmins, boxendmins))
5275                 {
5276                         // point trace
5277                         if (model->brushq3.submodel)
5278                         {
5279                                 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5280                                         if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5281                                                 Collision_TracePointBrushFloat(trace, boxstartmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5282                         }
5283                         else
5284                                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, ++markframe);
5285                 }
5286                 else
5287                 {
5288                         // line trace
5289                         if (model->brushq3.submodel)
5290                         {
5291                                 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5292                                         if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5293                                                 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5294                                 if (mod_q3bsp_curves_collisions.integer)
5295                                 {
5296                                         for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
5297                                         {
5298                                                 face = model->brushq3.data_thismodel->firstface + i;
5299                                                 if (face->collisions)
5300                                                         Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5301                                         }
5302                                 }
5303                         }
5304                         else
5305                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe, segmentmins, segmentmaxs);
5306                 }
5307         }
5308         else
5309         {
5310                 // box trace, performed as brush trace
5311                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
5312                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
5313                 if (model->brushq3.submodel)
5314                 {
5315                         for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5316                                 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5317                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5318                         if (mod_q3bsp_curves_collisions.integer)
5319                         {
5320                                 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
5321                                 {
5322                                         face = model->brushq3.data_thismodel->firstface + i;
5323                                         if (face->collisions)
5324                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5325                                 }
5326                         }
5327                 }
5328                 else
5329                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
5330         }
5331 }
5332
5333 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
5334 {
5335         int clusterindex, side, nodestackindex = 0;
5336         q3mnode_t *node, *nodestack[1024];
5337         node = model->brushq3.data_nodes;
5338         if (!model->brush.num_pvsclusters)
5339                 return true;
5340         for (;;)
5341         {
5342                 if (node->plane)
5343                 {
5344                         // node - recurse down the BSP tree
5345                         side = BoxOnPlaneSide(mins, maxs, node->plane) - 1;
5346                         if (side < 2)
5347                         {
5348                                 // box is on one side of plane, take that path
5349                                 node = node->children[side];
5350                         }
5351                         else
5352                         {
5353                                 // box crosses plane, take one path and remember the other
5354                                 if (nodestackindex < 1024)
5355                                         nodestack[nodestackindex++] = node->children[0];
5356                                 node = node->children[1];
5357                         }
5358                 }
5359                 else
5360                 {
5361                         // leaf - check cluster bit
5362                         clusterindex = ((q3mleaf_t *)node)->clusterindex;
5363 #if 0
5364                         if (clusterindex >= model->brush.num_pvsclusters)
5365                         {
5366                                 Con_Printf("%i >= %i\n", clusterindex, model->brush.num_pvsclusters);
5367                                 return true;
5368                         }
5369 #endif
5370                         if (CHECKPVSBIT(pvs, clusterindex))
5371                         {
5372                                 // it is visible, return immediately with the news
5373                                 return true;
5374                         }
5375                         else
5376                         {
5377                                 // nothing to see here, try another path we didn't take earlier
5378                                 if (nodestackindex == 0)
5379                                         break;
5380                                 node = nodestack[--nodestackindex];
5381                         }
5382                 }
5383         }
5384         // it is not visible
5385         return false;
5386 }
5387
5388 //Returns PVS data for a given point
5389 //(note: can return NULL)
5390 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
5391 {
5392         q3mnode_t *node;
5393         Mod_CheckLoaded(model);
5394         node = model->brushq3.data_nodes;
5395         while (node->plane)
5396                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
5397         if (((q3mleaf_t *)node)->clusterindex >= 0)
5398                 return model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5399         else
5400                 return NULL;
5401 }
5402
5403 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
5404 {
5405         while (node->plane)
5406         {
5407                 float d = PlaneDiff(org, node->plane);
5408                 if (d > radius)
5409                         node = node->children[0];
5410                 else if (d < -radius)
5411                         node = node->children[1];
5412                 else
5413                 {
5414                         // go down both sides
5415                         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
5416                         node = node->children[1];
5417                 }
5418         }
5419         // if this leaf is in a cluster, accumulate the pvs bits
5420         if (((q3mleaf_t *)node)->clusterindex >= 0)
5421         {
5422                 int i;
5423                 qbyte *pvs = model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5424                 for (i = 0;i < pvsbytes;i++)
5425                         pvsbuffer[i] |= pvs[i];
5426         }
5427 }
5428
5429 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
5430 //of the given point.
5431 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
5432 {
5433         int bytes = model->brush.num_pvsclusterbytes;
5434         bytes = min(bytes, pvsbufferlength);
5435         if (r_novis.integer || !model->brush.num_pvsclusters || !Mod_Q3BSP_GetPVS(model, org))
5436         {
5437                 memset(pvsbuffer, 0xFF, bytes);
5438                 return bytes;
5439         }
5440         memset(pvsbuffer, 0, bytes);
5441         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
5442         return bytes;
5443 }
5444
5445
5446 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
5447 {
5448         int supercontents = 0;
5449         if (nativecontents & Q2CONTENTS_SOLID)
5450                 supercontents |= SUPERCONTENTS_SOLID;
5451         if (nativecontents & Q2CONTENTS_WATER)
5452                 supercontents |= SUPERCONTENTS_WATER;
5453         if (nativecontents & Q2CONTENTS_SLIME)
5454                 supercontents |= SUPERCONTENTS_SLIME;
5455         if (nativecontents & Q2CONTENTS_LAVA)
5456                 supercontents |= SUPERCONTENTS_LAVA;
5457         return supercontents;
5458 }
5459
5460 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
5461 {
5462         int nativecontents = 0;
5463         if (supercontents & SUPERCONTENTS_SOLID)
5464                 nativecontents |= Q2CONTENTS_SOLID;
5465         if (supercontents & SUPERCONTENTS_WATER)
5466                 nativecontents |= Q2CONTENTS_WATER;
5467         if (supercontents & SUPERCONTENTS_SLIME)
5468                 nativecontents |= Q2CONTENTS_SLIME;
5469         if (supercontents & SUPERCONTENTS_LAVA)
5470                 nativecontents |= Q2CONTENTS_LAVA;
5471         return nativecontents;
5472 }
5473
5474 /*
5475 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)
5476 {
5477         mleaf_t *leaf;
5478         for (;;)
5479         {
5480                 if (!BoxesOverlap(node->mins, node->maxs, mins, maxs))
5481                         return;
5482                 if (!node->plane)
5483                         break;
5484                 Mod_Q3BSP_RecursiveGetVisible(node->children[0], model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
5485                 node = node->children[1];
5486         }
5487         leaf = (mleaf_t *)node;
5488         if ((pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
5489         {
5490                 int marksurfacenum;
5491                 q3msurface_t *surf;
5492                 if (maxleafs && *numleafs < maxleafs)
5493                         leaflist[(*numleaf)++] = leaf;
5494                 if (maxsurfaces)
5495                 {
5496                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
5497                         {
5498                                 face = leaf->firstleafface[marksurfacenum];
5499                                 if (face->shadowmark != shadowmarkcount)
5500                                 {
5501                                         face->shadowmark = shadowmarkcount;
5502                                         if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces)
5503                                                 surfacelist[(*numsurfaces)++] = face;
5504                                 }
5505                         }
5506                 }
5507         }
5508 }
5509
5510 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)
5511 {
5512         // FIXME: support portals
5513         if (maxsurfaces)
5514                 *numsurfaces = 0;
5515         if (maxleafs)
5516                 *numleafs = 0;
5517         if (model->submodel)
5518         {
5519                 if (maxsurfaces)
5520                 {
5521                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
5522                         {
5523                                 face = ent->model->brushq3.surfaces + leaf->firstmarksurface[marksurfacenum];
5524                                 if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces)
5525                                         surfacelist[(*numsurfaces)++] = face;
5526                         }
5527                 }
5528         }
5529         else
5530         {
5531                 pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
5532                 Mod_Q3BSP_RecursiveGetVisible(ent->model->brushq3.data_nodes, model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
5533         }
5534 }
5535 */
5536
5537 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
5538 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
5539 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);
5540 extern void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
5541 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);
5542 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
5543 {
5544         int i, j, numshadowmeshtriangles;
5545         q3dheader_t *header;
5546         float corner[3], yawradius, modelradius;
5547         q3msurface_t *face;
5548
5549         mod->type = mod_brushq3;
5550         mod->numframes = 1;
5551         mod->numskins = 1;
5552
5553         header = (q3dheader_t *)buffer;
5554
5555         i = LittleLong(header->version);
5556         if (i != Q3BSPVERSION)
5557                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
5558         if (mod->isworldmodel)
5559         {
5560                 Cvar_SetValue("halflifebsp", false);
5561                 // until we get a texture for it...
5562                 R_ResetQuakeSky();
5563         }
5564
5565         mod->soundfromcenter = true;
5566         mod->TraceBox = Mod_Q3BSP_TraceBox;
5567         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
5568         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
5569         mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
5570         mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
5571         mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
5572         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
5573         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5574         //mod->DrawSky = R_Q3BSP_DrawSky;
5575         mod->Draw = R_Q3BSP_Draw;
5576         mod->GetLightInfo = R_Q3BSP_GetLightInfo;
5577         mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
5578         mod->DrawLight = R_Q3BSP_DrawLight;
5579
5580         mod_base = (qbyte *)header;
5581
5582         // swap all the lumps
5583         for (i = 0;i < (int) sizeof(*header) / 4;i++)
5584                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
5585
5586         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
5587         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
5588         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
5589         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
5590         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
5591         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
5592         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
5593         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
5594         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
5595         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
5596         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
5597         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
5598         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
5599         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
5600         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
5601         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
5602         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
5603         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
5604
5605         // make a single combined shadow mesh to allow optimized shadow volume creation
5606         numshadowmeshtriangles = 0;
5607         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5608         {
5609                 face->num_firstshadowmeshtriangle = numshadowmeshtriangles;
5610                 numshadowmeshtriangles += face->num_triangles;
5611         }
5612         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
5613         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5614                 Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
5615         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
5616         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
5617         
5618         for (i = 0;i < loadmodel->brushq3.num_models;i++)
5619         {
5620                 if (i == 0)
5621                         mod = loadmodel;
5622                 else
5623                 {
5624                         char name[10];
5625                         // LordHavoc: only register submodels if it is the world
5626                         // (prevents bsp models from replacing world submodels)
5627                         if (!loadmodel->isworldmodel)
5628                                 continue;
5629                         // duplicate the basic information
5630                         sprintf(name, "*%i", i);
5631                         mod = Mod_FindName(name);
5632                         *mod = *loadmodel;
5633                         strcpy(mod->name, name);
5634                         // textures and memory belong to the main model
5635                         mod->texturepool = NULL;
5636                         mod->mempool = NULL;
5637                         mod->brush.GetPVS = NULL;
5638                         mod->brush.FatPVS = NULL;
5639                         mod->brush.BoxTouchingPVS = NULL;
5640                         mod->brush.LightPoint = NULL;
5641                         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5642                 }
5643                 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
5644                 mod->brushq3.submodel = i;
5645
5646                 // make the model surface list (used by shadowing/lighting)
5647                 mod->nummodelsurfaces = mod->brushq3.data_thismodel->numfaces;
5648                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
5649                 for (j = 0;j < mod->nummodelsurfaces;j++)
5650                         mod->surfacelist[j] = (mod->brushq3.data_thismodel->firstface - mod->brushq3.data_faces) + j;
5651
5652                 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
5653                 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
5654                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
5655                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
5656                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
5657                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
5658                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
5659                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
5660                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
5661                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
5662                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
5663                 mod->yawmins[2] = mod->normalmins[2];
5664                 mod->yawmaxs[2] = mod->normalmaxs[2];
5665                 mod->radius = modelradius;
5666                 mod->radius2 = modelradius * modelradius;
5667
5668                 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
5669                         if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
5670                                 break;
5671                 if (j < mod->brushq3.data_thismodel->numfaces)
5672                         mod->DrawSky = R_Q3BSP_DrawSky;
5673         }
5674 }
5675
5676 void Mod_IBSP_Load(model_t *mod, void *buffer)
5677 {
5678         int i = LittleLong(((int *)buffer)[1]);
5679         if (i == Q3BSPVERSION)
5680                 Mod_Q3BSP_Load(mod,buffer);
5681         else if (i == Q2BSPVERSION)
5682                 Mod_Q2BSP_Load(mod,buffer);
5683         else
5684                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
5685 }
5686
5687 void Mod_MAP_Load(model_t *mod, void *buffer)
5688 {
5689         Host_Error("Mod_MAP_Load: not yet implemented\n");
5690 }
5691