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