]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
360
[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                 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1206         }
1207         else // LordHavoc: bsp version 29 (normal white lighting)
1208         {
1209                 // LordHavoc: hope is not lost yet, check for a .lit file to load
1210                 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1211                 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1212                 strlcat (litfilename, ".lit", sizeof (litfilename));
1213                 data = (qbyte*) FS_LoadFile(litfilename, tempmempool, false);
1214                 if (data)
1215                 {
1216                         if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1217                         {
1218                                 i = LittleLong(((int *)data)[1]);
1219                                 if (i == 1)
1220                                 {
1221                                         Con_DPrintf("loaded %s\n", litfilename);
1222                                         loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1223                                         memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1224                                         Mem_Free(data);
1225                                         return;
1226                                 }
1227                                 else
1228                                 {
1229                                         Con_Printf("Unknown .lit file version (%d)\n", i);
1230                                         Mem_Free(data);
1231                                 }
1232                         }
1233                         else
1234                         {
1235                                 if (fs_filesize == 8)
1236                                         Con_Print("Empty .lit file, ignoring\n");
1237                                 else
1238                                         Con_Print("Corrupt .lit file (old version?), ignoring\n");
1239                                 Mem_Free(data);
1240                         }
1241                 }
1242                 // LordHavoc: oh well, expand the white lighting data
1243                 if (!l->filelen)
1244                         return;
1245                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1246                 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1247                 out = loadmodel->brushq1.lightdata;
1248                 memcpy(in, mod_base + l->fileofs, l->filelen);
1249                 for (i = 0;i < l->filelen;i++)
1250                 {
1251                         d = *in++;
1252                         *out++ = d;
1253                         *out++ = d;
1254                         *out++ = d;
1255                 }
1256         }
1257 }
1258
1259 static void Mod_Q1BSP_LoadLightList(void)
1260 {
1261         int a, n, numlights;
1262         char lightsfilename[1024], *s, *t, *lightsstring;
1263         mlight_t *e;
1264
1265         strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1266         FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1267         strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1268         s = lightsstring = (char *) FS_LoadFile(lightsfilename, tempmempool, false);
1269         if (s)
1270         {
1271                 numlights = 0;
1272                 while (*s)
1273                 {
1274                         while (*s && *s != '\n')
1275                                 s++;
1276                         if (!*s)
1277                         {
1278                                 Mem_Free(lightsstring);
1279                                 Host_Error("lights file must end with a newline\n");
1280                         }
1281                         s++;
1282                         numlights++;
1283                 }
1284                 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1285                 s = lightsstring;
1286                 n = 0;
1287                 while (*s && n < numlights)
1288                 {
1289                         t = s;
1290                         while (*s && *s != '\n')
1291                                 s++;
1292                         if (!*s)
1293                         {
1294                                 Mem_Free(lightsstring);
1295                                 Host_Error("misparsed lights file!\n");
1296                         }
1297                         e = loadmodel->brushq1.lights + n;
1298                         *s = 0;
1299                         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);
1300                         *s = '\n';
1301                         if (a != 14)
1302                         {
1303                                 Mem_Free(lightsstring);
1304                                 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);
1305                         }
1306                         s++;
1307                         n++;
1308                 }
1309                 if (*s)
1310                 {
1311                         Mem_Free(lightsstring);
1312                         Host_Error("misparsed lights file!\n");
1313                 }
1314                 loadmodel->brushq1.numlights = numlights;
1315                 Mem_Free(lightsstring);
1316         }
1317 }
1318
1319 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1320 {
1321         loadmodel->brushq1.num_compressedpvs = 0;
1322         loadmodel->brushq1.data_compressedpvs = NULL;
1323         if (!l->filelen)
1324                 return;
1325         loadmodel->brushq1.num_compressedpvs = l->filelen;
1326         loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1327         memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1328 }
1329
1330 // used only for HalfLife maps
1331 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1332 {
1333         char key[128], value[4096];
1334         char wadname[128];
1335         int i, j, k;
1336         if (!data)
1337                 return;
1338         if (!COM_ParseToken(&data, false))
1339                 return; // error
1340         if (com_token[0] != '{')
1341                 return; // error
1342         while (1)
1343         {
1344                 if (!COM_ParseToken(&data, false))
1345                         return; // error
1346                 if (com_token[0] == '}')
1347                         break; // end of worldspawn
1348                 if (com_token[0] == '_')
1349                         strcpy(key, com_token + 1);
1350                 else
1351                         strcpy(key, com_token);
1352                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1353                         key[strlen(key)-1] = 0;
1354                 if (!COM_ParseToken(&data, false))
1355                         return; // error
1356                 strcpy(value, com_token);
1357                 if (!strcmp("wad", key)) // for HalfLife maps
1358                 {
1359                         if (loadmodel->brush.ishlbsp)
1360                         {
1361                                 j = 0;
1362                                 for (i = 0;i < 4096;i++)
1363                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1364                                                 break;
1365                                 if (value[i])
1366                                 {
1367                                         for (;i < 4096;i++)
1368                                         {
1369                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1370                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1371                                                         j = i+1;
1372                                                 else if (value[i] == ';' || value[i] == 0)
1373                                                 {
1374                                                         k = value[i];
1375                                                         value[i] = 0;
1376                                                         strcpy(wadname, "textures/");
1377                                                         strcat(wadname, &value[j]);
1378                                                         W_LoadTextureWadFile(wadname, false);
1379                                                         j = i+1;
1380                                                         if (!k)
1381                                                                 break;
1382                                                 }
1383                                         }
1384                                 }
1385                         }
1386                 }
1387         }
1388 }
1389
1390 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1391 {
1392         loadmodel->brush.entities = NULL;
1393         if (!l->filelen)
1394                 return;
1395         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1396         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1397         if (loadmodel->brush.ishlbsp)
1398                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1399 }
1400
1401
1402 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1403 {
1404         dvertex_t       *in;
1405         mvertex_t       *out;
1406         int                     i, count;
1407
1408         in = (void *)(mod_base + l->fileofs);
1409         if (l->filelen % sizeof(*in))
1410                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1411         count = l->filelen / sizeof(*in);
1412         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1413
1414         loadmodel->brushq1.vertexes = out;
1415         loadmodel->brushq1.numvertexes = count;
1416
1417         for ( i=0 ; i<count ; i++, in++, out++)
1418         {
1419                 out->position[0] = LittleFloat(in->point[0]);
1420                 out->position[1] = LittleFloat(in->point[1]);
1421                 out->position[2] = LittleFloat(in->point[2]);
1422         }
1423 }
1424
1425 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1426 {
1427         dmodel_t        *in;
1428         dmodel_t        *out;
1429         int                     i, j, count;
1430
1431         in = (void *)(mod_base + l->fileofs);
1432         if (l->filelen % sizeof(*in))
1433                 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1434         count = l->filelen / sizeof(*in);
1435         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1436
1437         loadmodel->brushq1.submodels = out;
1438         loadmodel->brush.numsubmodels = count;
1439
1440         for ( i=0 ; i<count ; i++, in++, out++)
1441         {
1442                 for (j=0 ; j<3 ; j++)
1443                 {
1444                         // spread the mins / maxs by a pixel
1445                         out->mins[j] = LittleFloat(in->mins[j]) - 1;
1446                         out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1447                         out->origin[j] = LittleFloat(in->origin[j]);
1448                 }
1449                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1450                         out->headnode[j] = LittleLong(in->headnode[j]);
1451                 out->visleafs = LittleLong(in->visleafs);
1452                 out->firstface = LittleLong(in->firstface);
1453                 out->numfaces = LittleLong(in->numfaces);
1454         }
1455 }
1456
1457 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1458 {
1459         dedge_t *in;
1460         medge_t *out;
1461         int     i, count;
1462
1463         in = (void *)(mod_base + l->fileofs);
1464         if (l->filelen % sizeof(*in))
1465                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1466         count = l->filelen / sizeof(*in);
1467         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1468
1469         loadmodel->brushq1.edges = out;
1470         loadmodel->brushq1.numedges = count;
1471
1472         for ( i=0 ; i<count ; i++, in++, out++)
1473         {
1474                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1475                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1476         }
1477 }
1478
1479 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1480 {
1481         texinfo_t *in;
1482         mtexinfo_t *out;
1483         int i, j, k, count, miptex;
1484
1485         in = (void *)(mod_base + l->fileofs);
1486         if (l->filelen % sizeof(*in))
1487                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1488         count = l->filelen / sizeof(*in);
1489         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1490
1491         loadmodel->brushq1.texinfo = out;
1492         loadmodel->brushq1.numtexinfo = count;
1493
1494         for (i = 0;i < count;i++, in++, out++)
1495         {
1496                 for (k = 0;k < 2;k++)
1497                         for (j = 0;j < 4;j++)
1498                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1499
1500                 miptex = LittleLong(in->miptex);
1501                 out->flags = LittleLong(in->flags);
1502
1503                 out->texture = NULL;
1504                 if (loadmodel->brushq1.textures)
1505                 {
1506                         if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1507                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1508                         else
1509                                 out->texture = loadmodel->brushq1.textures + miptex;
1510                 }
1511                 if (out->flags & TEX_SPECIAL)
1512                 {
1513                         // if texture chosen is NULL or the shader needs a lightmap,
1514                         // force to notexture water shader
1515                         if (out->texture == NULL || out->texture->flags & SURF_LIGHTMAP)
1516                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1517                 }
1518                 else
1519                 {
1520                         // if texture chosen is NULL, force to notexture
1521                         if (out->texture == NULL)
1522                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1523                 }
1524         }
1525 }
1526
1527 #if 0
1528 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1529 {
1530         int             i, j;
1531         float   *v;
1532
1533         mins[0] = mins[1] = mins[2] = 9999;
1534         maxs[0] = maxs[1] = maxs[2] = -9999;
1535         v = verts;
1536         for (i = 0;i < numverts;i++)
1537         {
1538                 for (j = 0;j < 3;j++, v++)
1539                 {
1540                         if (*v < mins[j])
1541                                 mins[j] = *v;
1542                         if (*v > maxs[j])
1543                                 maxs[j] = *v;
1544                 }
1545         }
1546 }
1547
1548 #define MAX_SUBDIVPOLYTRIANGLES 4096
1549 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1550
1551 static int subdivpolyverts, subdivpolytriangles;
1552 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1553 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1554
1555 static int subdivpolylookupvert(vec3_t v)
1556 {
1557         int i;
1558         for (i = 0;i < subdivpolyverts;i++)
1559                 if (subdivpolyvert[i][0] == v[0]
1560                  && subdivpolyvert[i][1] == v[1]
1561                  && subdivpolyvert[i][2] == v[2])
1562                         return i;
1563         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1564                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1565         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1566         return subdivpolyverts++;
1567 }
1568
1569 static void SubdividePolygon(int numverts, float *verts)
1570 {
1571         int             i, i1, i2, i3, f, b, c, p;
1572         vec3_t  mins, maxs, front[256], back[256];
1573         float   m, *pv, *cv, dist[256], frac;
1574
1575         if (numverts > 250)
1576                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1577
1578         BoundPoly(numverts, verts, mins, maxs);
1579
1580         for (i = 0;i < 3;i++)
1581         {
1582                 m = (mins[i] + maxs[i]) * 0.5;
1583                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1584                 if (maxs[i] - m < 8)
1585                         continue;
1586                 if (m - mins[i] < 8)
1587                         continue;
1588
1589                 // cut it
1590                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1591                         dist[c] = cv[i] - m;
1592
1593                 f = b = 0;
1594                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1595                 {
1596                         if (dist[p] >= 0)
1597                         {
1598                                 VectorCopy(pv, front[f]);
1599                                 f++;
1600                         }
1601                         if (dist[p] <= 0)
1602                         {
1603                                 VectorCopy(pv, back[b]);
1604                                 b++;
1605                         }
1606                         if (dist[p] == 0 || dist[c] == 0)
1607                                 continue;
1608                         if ((dist[p] > 0) != (dist[c] > 0) )
1609                         {
1610                                 // clip point
1611                                 frac = dist[p] / (dist[p] - dist[c]);
1612                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1613                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1614                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1615                                 f++;
1616                                 b++;
1617                         }
1618                 }
1619
1620                 SubdividePolygon(f, front[0]);
1621                 SubdividePolygon(b, back[0]);
1622                 return;
1623         }
1624
1625         i1 = subdivpolylookupvert(verts);
1626         i2 = subdivpolylookupvert(verts + 3);
1627         for (i = 2;i < numverts;i++)
1628         {
1629                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1630                 {
1631                         Con_Print("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1632                         return;
1633                 }
1634
1635                 i3 = subdivpolylookupvert(verts + i * 3);
1636                 subdivpolyindex[subdivpolytriangles][0] = i1;
1637                 subdivpolyindex[subdivpolytriangles][1] = i2;
1638                 subdivpolyindex[subdivpolytriangles][2] = i3;
1639                 i2 = i3;
1640                 subdivpolytriangles++;
1641         }
1642 }
1643
1644 //Breaks a polygon up along axial 64 unit
1645 //boundaries so that turbulent and sky warps
1646 //can be done reasonably.
1647 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1648 {
1649         int i, j;
1650         surfvertex_t *v;
1651         surfmesh_t *mesh;
1652
1653         subdivpolytriangles = 0;
1654         subdivpolyverts = 0;
1655         SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1656         if (subdivpolytriangles < 1)
1657                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1658
1659         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1660         mesh->num_vertices = subdivpolyverts;
1661         mesh->num_triangles = subdivpolytriangles;
1662         mesh->vertex = (surfvertex_t *)(mesh + 1);
1663         mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1664         memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1665
1666         for (i = 0;i < mesh->num_triangles;i++)
1667                 for (j = 0;j < 3;j++)
1668                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1669
1670         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1671         {
1672                 VectorCopy(subdivpolyvert[i], v->v);
1673                 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1674                 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1675         }
1676 }
1677 #endif
1678
1679 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1680 {
1681         surfmesh_t *mesh;
1682         mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1683         mesh->num_vertices = numverts;
1684         mesh->num_triangles = numtriangles;
1685         mesh->data_vertex3f = (float *)(mesh + 1);
1686         mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1687         mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1688         mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1689         mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1690         mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1691         mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1692         mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1693         mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1694         mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1695         return mesh;
1696 }
1697
1698 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1699 {
1700         int i, lindex, j;
1701         float *vec, *vert, mins[3], maxs[3], val, *v;
1702         mtexinfo_t *tex;
1703
1704         // convert edges back to a normal polygon
1705         surf->poly_numverts = numedges;
1706         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1707         for (i = 0;i < numedges;i++)
1708         {
1709                 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1710                 if (lindex > 0)
1711                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1712                 else
1713                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1714                 VectorCopy(vec, vert);
1715                 vert += 3;
1716         }
1717
1718         // calculate polygon bounding box and center
1719         vert = surf->poly_verts;
1720         VectorCopy(vert, mins);
1721         VectorCopy(vert, maxs);
1722         vert += 3;
1723         for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1724         {
1725                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1726                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1727                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1728         }
1729         VectorCopy(mins, surf->poly_mins);
1730         VectorCopy(maxs, surf->poly_maxs);
1731         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1732         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1733         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1734
1735         // generate surface extents information
1736         tex = surf->texinfo;
1737         mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1738         mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1739         for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1740         {
1741                 for (j = 0;j < 2;j++)
1742                 {
1743                         val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1744                         if (mins[j] > val)
1745                                 mins[j] = val;
1746                         if (maxs[j] < val)
1747                                 maxs[j] = val;
1748                 }
1749         }
1750         for (i = 0;i < 2;i++)
1751         {
1752                 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1753                 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1754         }
1755 }
1756
1757 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1758 {
1759         dface_t *in;
1760         msurface_t *surf;
1761         int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1762         surfmesh_t *mesh;
1763         float s, t;
1764
1765         in = (void *)(mod_base + l->fileofs);
1766         if (l->filelen % sizeof(*in))
1767                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1768         count = l->filelen / sizeof(*in);
1769         loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1770
1771         loadmodel->brushq1.numsurfaces = count;
1772         loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1773         loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1774         loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1775
1776         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++)
1777         {
1778                 surf->number = surfnum;
1779                 // FIXME: validate edges, texinfo, etc?
1780                 firstedge = LittleLong(in->firstedge);
1781                 numedges = LittleShort(in->numedges);
1782                 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)
1783                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1784                 i = LittleShort(in->texinfo);
1785                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1786                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1787                 surf->texinfo = loadmodel->brushq1.texinfo + i;
1788                 surf->flags = surf->texinfo->texture->flags;
1789
1790                 planenum = LittleShort(in->planenum);
1791                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1792                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1793
1794                 if (LittleShort(in->side))
1795                         surf->flags |= SURF_PLANEBACK;
1796
1797                 surf->plane = loadmodel->brushq1.planes + planenum;
1798
1799                 // clear lightmap (filled in later)
1800                 surf->lightmaptexture = NULL;
1801
1802                 // force lightmap upload on first time seeing the surface
1803                 surf->cached_dlight = true;
1804
1805                 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1806
1807                 ssize = (surf->extents[0] >> 4) + 1;
1808                 tsize = (surf->extents[1] >> 4) + 1;
1809
1810                 // lighting info
1811                 for (i = 0;i < MAXLIGHTMAPS;i++)
1812                         surf->styles[i] = in->styles[i];
1813                 i = LittleLong(in->lightofs);
1814                 if (i == -1)
1815                         surf->samples = NULL;
1816                 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1817                         surf->samples = loadmodel->brushq1.lightdata + i;
1818                 else // LordHavoc: white lighting (bsp version 29)
1819                         surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1820
1821                 if (surf->texinfo->texture->flags & SURF_LIGHTMAP)
1822                 {
1823                         if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1824                                 Host_Error("Bad surface extents");
1825                         // stainmap for permanent marks on walls
1826                         surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1827                         // clear to white
1828                         memset(surf->stainsamples, 255, ssize * tsize * 3);
1829                 }
1830         }
1831
1832         loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1833
1834         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++)
1835         {
1836                 mesh = &surf->mesh;
1837                 mesh->num_vertices = surf->poly_numverts;
1838                 mesh->num_triangles = surf->poly_numverts - 2;
1839                 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1840                 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1841                 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1842                 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1843                 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1844                 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1845                 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1846                 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1847                 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1848                 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1849
1850                 surf->lightmaptexturestride = 0;
1851                 surf->lightmaptexture = NULL;
1852
1853                 for (i = 0;i < mesh->num_vertices;i++)
1854                 {
1855                         mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1856                         mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1857                         mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1858                         s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1859                         t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1860                         mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1861                         mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1862                         mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1863                         mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1864                         mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1865                         mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1866                         mesh->data_lightmapoffsets[i] = 0;
1867                 }
1868
1869                 for (i = 0;i < mesh->num_triangles;i++)
1870                 {
1871                         mesh->data_element3i[i * 3 + 0] = 0;
1872                         mesh->data_element3i[i * 3 + 1] = i + 1;
1873                         mesh->data_element3i[i * 3 + 2] = i + 2;
1874                 }
1875
1876                 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1877                 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);
1878
1879                 if (surf->texinfo->texture->flags & SURF_LIGHTMAP)
1880                 {
1881                         int i, iu, iv, smax, tmax;
1882                         float u, v, ubase, vbase, uscale, vscale;
1883
1884                         smax = surf->extents[0] >> 4;
1885                         tmax = surf->extents[1] >> 4;
1886
1887                         if (r_miplightmaps.integer)
1888                         {
1889                                 surf->lightmaptexturestride = smax+1;
1890                                 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);
1891                         }
1892                         else
1893                         {
1894                                 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1895                                 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);
1896                         }
1897                         R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1898                         uscale = (uscale - ubase) / (smax + 1);
1899                         vscale = (vscale - vbase) / (tmax + 1);
1900
1901                         for (i = 0;i < mesh->num_vertices;i++)
1902                         {
1903                                 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1904                                 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1905                                 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1906                                 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1907                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1908                                 iu = (int) u;
1909                                 iv = (int) v;
1910                                 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1911                         }
1912                 }
1913         }
1914 }
1915
1916 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1917 {
1918         node->parent = parent;
1919         if (node->contents < 0)
1920                 return;
1921         Mod_Q1BSP_SetParent(node->children[0], node);
1922         Mod_Q1BSP_SetParent(node->children[1], node);
1923 }
1924
1925 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1926 {
1927         int                     i, j, count, p;
1928         dnode_t         *in;
1929         mnode_t         *out;
1930
1931         in = (void *)(mod_base + l->fileofs);
1932         if (l->filelen % sizeof(*in))
1933                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1934         count = l->filelen / sizeof(*in);
1935         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1936
1937         loadmodel->brushq1.nodes = out;
1938         loadmodel->brushq1.numnodes = count;
1939
1940         for ( i=0 ; i<count ; i++, in++, out++)
1941         {
1942                 for (j=0 ; j<3 ; j++)
1943                 {
1944                         out->mins[j] = LittleShort(in->mins[j]);
1945                         out->maxs[j] = LittleShort(in->maxs[j]);
1946                 }
1947
1948                 p = LittleLong(in->planenum);
1949                 out->plane = loadmodel->brushq1.planes + p;
1950
1951                 out->firstsurface = LittleShort(in->firstface);
1952                 out->numsurfaces = LittleShort(in->numfaces);
1953
1954                 for (j=0 ; j<2 ; j++)
1955                 {
1956                         p = LittleShort(in->children[j]);
1957                         if (p >= 0)
1958                                 out->children[j] = loadmodel->brushq1.nodes + p;
1959                         else
1960                                 out->children[j] = (mnode_t *)(loadmodel->brushq1.data_leafs + (-1 - p));
1961                 }
1962         }
1963
1964         Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL);    // sets nodes and leafs
1965 }
1966
1967 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1968 {
1969         dleaf_t *in;
1970         mleaf_t *out;
1971         int i, j, count, p;
1972
1973         in = (void *)(mod_base + l->fileofs);
1974         if (l->filelen % sizeof(*in))
1975                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1976         count = l->filelen / sizeof(*in);
1977         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1978
1979         loadmodel->brushq1.data_leafs = out;
1980         loadmodel->brushq1.num_leafs = count;
1981         // get visleafs from the submodel data
1982         loadmodel->brush.num_pvsclusters = loadmodel->brushq1.submodels[0].visleafs;
1983         loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters+7)>>3;
1984         loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
1985         memset(loadmodel->brush.data_pvsclusters, 0xFF, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
1986
1987         for ( i=0 ; i<count ; i++, in++, out++)
1988         {
1989                 for (j=0 ; j<3 ; j++)
1990                 {
1991                         out->mins[j] = LittleShort(in->mins[j]);
1992                         out->maxs[j] = LittleShort(in->maxs[j]);
1993                 }
1994
1995                 // FIXME: this function could really benefit from some error checking
1996
1997                 out->contents = LittleLong(in->contents);
1998
1999                 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
2000                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
2001                 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
2002                 {
2003                         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);
2004                         out->firstmarksurface = NULL;
2005                         out->nummarksurfaces = 0;
2006                 }
2007
2008                 out->clusterindex = i - 1;
2009                 if (out->clusterindex >= loadmodel->brush.num_pvsclusters)
2010                         out->clusterindex = -1;
2011
2012                 p = LittleLong(in->visofs);
2013                 // ignore visofs errors on leaf 0 (solid)
2014                 if (p >= 0 && out->clusterindex >= 0)
2015                 {
2016                         if (p >= loadmodel->brushq1.num_compressedpvs)
2017                                 Con_Print("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
2018                         else
2019                                 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);
2020                 }
2021
2022                 for (j = 0;j < 4;j++)
2023                         out->ambient_sound_level[j] = in->ambient_level[j];
2024
2025                 // FIXME: Insert caustics here
2026         }
2027 }
2028
2029 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
2030 {
2031         dclipnode_t *in, *out;
2032         int                     i, count;
2033         hull_t          *hull;
2034
2035         in = (void *)(mod_base + l->fileofs);
2036         if (l->filelen % sizeof(*in))
2037                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2038         count = l->filelen / sizeof(*in);
2039         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2040
2041         loadmodel->brushq1.clipnodes = out;
2042         loadmodel->brushq1.numclipnodes = count;
2043
2044         if (loadmodel->brush.ishlbsp)
2045         {
2046                 hull = &loadmodel->brushq1.hulls[1];
2047                 hull->clipnodes = out;
2048                 hull->firstclipnode = 0;
2049                 hull->lastclipnode = count-1;
2050                 hull->planes = loadmodel->brushq1.planes;
2051                 hull->clip_mins[0] = -16;
2052                 hull->clip_mins[1] = -16;
2053                 hull->clip_mins[2] = -36;
2054                 hull->clip_maxs[0] = 16;
2055                 hull->clip_maxs[1] = 16;
2056                 hull->clip_maxs[2] = 36;
2057                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2058
2059                 hull = &loadmodel->brushq1.hulls[2];
2060                 hull->clipnodes = out;
2061                 hull->firstclipnode = 0;
2062                 hull->lastclipnode = count-1;
2063                 hull->planes = loadmodel->brushq1.planes;
2064                 hull->clip_mins[0] = -32;
2065                 hull->clip_mins[1] = -32;
2066                 hull->clip_mins[2] = -32;
2067                 hull->clip_maxs[0] = 32;
2068                 hull->clip_maxs[1] = 32;
2069                 hull->clip_maxs[2] = 32;
2070                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2071
2072                 hull = &loadmodel->brushq1.hulls[3];
2073                 hull->clipnodes = out;
2074                 hull->firstclipnode = 0;
2075                 hull->lastclipnode = count-1;
2076                 hull->planes = loadmodel->brushq1.planes;
2077                 hull->clip_mins[0] = -16;
2078                 hull->clip_mins[1] = -16;
2079                 hull->clip_mins[2] = -18;
2080                 hull->clip_maxs[0] = 16;
2081                 hull->clip_maxs[1] = 16;
2082                 hull->clip_maxs[2] = 18;
2083                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2084         }
2085         else
2086         {
2087                 hull = &loadmodel->brushq1.hulls[1];
2088                 hull->clipnodes = out;
2089                 hull->firstclipnode = 0;
2090                 hull->lastclipnode = count-1;
2091                 hull->planes = loadmodel->brushq1.planes;
2092                 hull->clip_mins[0] = -16;
2093                 hull->clip_mins[1] = -16;
2094                 hull->clip_mins[2] = -24;
2095                 hull->clip_maxs[0] = 16;
2096                 hull->clip_maxs[1] = 16;
2097                 hull->clip_maxs[2] = 32;
2098                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2099
2100                 hull = &loadmodel->brushq1.hulls[2];
2101                 hull->clipnodes = out;
2102                 hull->firstclipnode = 0;
2103                 hull->lastclipnode = count-1;
2104                 hull->planes = loadmodel->brushq1.planes;
2105                 hull->clip_mins[0] = -32;
2106                 hull->clip_mins[1] = -32;
2107                 hull->clip_mins[2] = -24;
2108                 hull->clip_maxs[0] = 32;
2109                 hull->clip_maxs[1] = 32;
2110                 hull->clip_maxs[2] = 64;
2111                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2112         }
2113
2114         for (i=0 ; i<count ; i++, out++, in++)
2115         {
2116                 out->planenum = LittleLong(in->planenum);
2117                 out->children[0] = LittleShort(in->children[0]);
2118                 out->children[1] = LittleShort(in->children[1]);
2119                 if (out->children[0] >= count || out->children[1] >= count)
2120                         Host_Error("Corrupt clipping hull(out of range child)\n");
2121         }
2122 }
2123
2124 //Duplicate the drawing hull structure as a clipping hull
2125 static void Mod_Q1BSP_MakeHull0(void)
2126 {
2127         mnode_t         *in;
2128         dclipnode_t *out;
2129         int                     i;
2130         hull_t          *hull;
2131
2132         hull = &loadmodel->brushq1.hulls[0];
2133
2134         in = loadmodel->brushq1.nodes;
2135         out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
2136
2137         hull->clipnodes = out;
2138         hull->firstclipnode = 0;
2139         hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
2140         hull->planes = loadmodel->brushq1.planes;
2141
2142         for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
2143         {
2144                 out->planenum = in->plane - loadmodel->brushq1.planes;
2145                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
2146                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
2147         }
2148 }
2149
2150 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
2151 {
2152         int i, j;
2153         short *in;
2154
2155         in = (void *)(mod_base + l->fileofs);
2156         if (l->filelen % sizeof(*in))
2157                 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2158         loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2159         loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2160
2161         for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2162         {
2163                 j = (unsigned) LittleShort(in[i]);
2164                 if (j >= loadmodel->brushq1.numsurfaces)
2165                         Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2166                 loadmodel->brushq1.marksurfaces[i] = j;
2167         }
2168 }
2169
2170 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2171 {
2172         int             i;
2173         int             *in;
2174
2175         in = (void *)(mod_base + l->fileofs);
2176         if (l->filelen % sizeof(*in))
2177                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2178         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2179         loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2180
2181         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2182                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2183 }
2184
2185
2186 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2187 {
2188         int                     i;
2189         mplane_t        *out;
2190         dplane_t        *in;
2191
2192         in = (void *)(mod_base + l->fileofs);
2193         if (l->filelen % sizeof(*in))
2194                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2195
2196         loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2197         loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2198
2199         for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2200         {
2201                 out->normal[0] = LittleFloat(in->normal[0]);
2202                 out->normal[1] = LittleFloat(in->normal[1]);
2203                 out->normal[2] = LittleFloat(in->normal[2]);
2204                 out->dist = LittleFloat(in->dist);
2205
2206                 PlaneClassify(out);
2207         }
2208 }
2209
2210 #define MAX_PORTALPOINTS 64
2211
2212 typedef struct portal_s
2213 {
2214         mplane_t plane;
2215         mnode_t *nodes[2];              // [0] = front side of plane
2216         struct portal_s *next[2];
2217         int numpoints;
2218         double points[3*MAX_PORTALPOINTS];
2219         struct portal_s *chain; // all portals are linked into a list
2220 }
2221 portal_t;
2222
2223 static portal_t *portalchain;
2224
2225 /*
2226 ===========
2227 AllocPortal
2228 ===========
2229 */
2230 static portal_t *AllocPortal(void)
2231 {
2232         portal_t *p;
2233         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2234         p->chain = portalchain;
2235         portalchain = p;
2236         return p;
2237 }
2238
2239 static void FreePortal(portal_t *p)
2240 {
2241         Mem_Free(p);
2242 }
2243
2244 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2245 {
2246         // calculate children first
2247         if (node->children[0]->contents >= 0)
2248                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2249         if (node->children[1]->contents >= 0)
2250                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2251
2252         // make combined bounding box from children
2253         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2254         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2255         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2256         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2257         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2258         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2259 }
2260
2261 static void Mod_Q1BSP_FinalizePortals(void)
2262 {
2263         int i, j, numportals, numpoints;
2264         portal_t *p, *pnext;
2265         mportal_t *portal;
2266         mvertex_t *point;
2267         mleaf_t *leaf, *endleaf;
2268
2269         // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2270         leaf = loadmodel->brushq1.data_leafs;
2271         endleaf = leaf + loadmodel->brushq1.num_leafs;
2272         for (;leaf < endleaf;leaf++)
2273         {
2274                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2275                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2276         }
2277         p = portalchain;
2278         while (p)
2279         {
2280                 if (p->numpoints >= 3)
2281                 {
2282                         for (i = 0;i < 2;i++)
2283                         {
2284                                 leaf = (mleaf_t *)p->nodes[i];
2285                                 for (j = 0;j < p->numpoints;j++)
2286                                 {
2287                                         if (leaf->mins[0] > p->points[j*3+0]) leaf->mins[0] = p->points[j*3+0];
2288                                         if (leaf->mins[1] > p->points[j*3+1]) leaf->mins[1] = p->points[j*3+1];
2289                                         if (leaf->mins[2] > p->points[j*3+2]) leaf->mins[2] = p->points[j*3+2];
2290                                         if (leaf->maxs[0] < p->points[j*3+0]) leaf->maxs[0] = p->points[j*3+0];
2291                                         if (leaf->maxs[1] < p->points[j*3+1]) leaf->maxs[1] = p->points[j*3+1];
2292                                         if (leaf->maxs[2] < p->points[j*3+2]) leaf->maxs[2] = p->points[j*3+2];
2293                                 }
2294                         }
2295                 }
2296                 p = p->chain;
2297         }
2298
2299         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2300
2301         // tally up portal and point counts
2302         p = portalchain;
2303         numportals = 0;
2304         numpoints = 0;
2305         while (p)
2306         {
2307                 // note: this check must match the one below or it will usually corrupt memory
2308                 // 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
2309                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1]
2310                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2311                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2312                 {
2313                         numportals += 2;
2314                         numpoints += p->numpoints * 2;
2315                 }
2316                 p = p->chain;
2317         }
2318         loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2319         loadmodel->brushq1.numportals = numportals;
2320         loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2321         loadmodel->brushq1.numportalpoints = numpoints;
2322         // clear all leaf portal chains
2323         for (i = 0;i < loadmodel->brushq1.num_leafs;i++)
2324                 loadmodel->brushq1.data_leafs[i].portals = NULL;
2325         // process all portals in the global portal chain, while freeing them
2326         portal = loadmodel->brushq1.portals;
2327         point = loadmodel->brushq1.portalpoints;
2328         p = portalchain;
2329         portalchain = NULL;
2330         while (p)
2331         {
2332                 pnext = p->chain;
2333
2334                 if (p->numpoints >= 3)
2335                 {
2336                         // note: this check must match the one above or it will usually corrupt memory
2337                         // 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
2338                         if (p->nodes[0] != p->nodes[1]
2339                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2340                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2341                         {
2342                                 // first make the back to front portal(forward portal)
2343                                 portal->points = point;
2344                                 portal->numpoints = p->numpoints;
2345                                 portal->plane.dist = p->plane.dist;
2346                                 VectorCopy(p->plane.normal, portal->plane.normal);
2347                                 portal->here = (mleaf_t *)p->nodes[1];
2348                                 portal->past = (mleaf_t *)p->nodes[0];
2349                                 // copy points
2350                                 for (j = 0;j < portal->numpoints;j++)
2351                                 {
2352                                         VectorCopy(p->points + j*3, point->position);
2353                                         point++;
2354                                 }
2355                                 PlaneClassify(&portal->plane);
2356
2357                                 // link into leaf's portal chain
2358                                 portal->next = portal->here->portals;
2359                                 portal->here->portals = portal;
2360
2361                                 // advance to next portal
2362                                 portal++;
2363
2364                                 // then make the front to back portal(backward portal)
2365                                 portal->points = point;
2366                                 portal->numpoints = p->numpoints;
2367                                 portal->plane.dist = -p->plane.dist;
2368                                 VectorNegate(p->plane.normal, portal->plane.normal);
2369                                 portal->here = (mleaf_t *)p->nodes[0];
2370                                 portal->past = (mleaf_t *)p->nodes[1];
2371                                 // copy points
2372                                 for (j = portal->numpoints - 1;j >= 0;j--)
2373                                 {
2374                                         VectorCopy(p->points + j*3, point->position);
2375                                         point++;
2376                                 }
2377                                 PlaneClassify(&portal->plane);
2378
2379                                 // link into leaf's portal chain
2380                                 portal->next = portal->here->portals;
2381                                 portal->here->portals = portal;
2382
2383                                 // advance to next portal
2384                                 portal++;
2385                         }
2386                 }
2387                 FreePortal(p);
2388                 p = pnext;
2389         }
2390 }
2391
2392 /*
2393 =============
2394 AddPortalToNodes
2395 =============
2396 */
2397 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2398 {
2399         if (!front)
2400                 Host_Error("AddPortalToNodes: NULL front node");
2401         if (!back)
2402                 Host_Error("AddPortalToNodes: NULL back node");
2403         if (p->nodes[0] || p->nodes[1])
2404                 Host_Error("AddPortalToNodes: already included");
2405         // 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
2406
2407         p->nodes[0] = front;
2408         p->next[0] = (portal_t *)front->portals;
2409         front->portals = (mportal_t *)p;
2410
2411         p->nodes[1] = back;
2412         p->next[1] = (portal_t *)back->portals;
2413         back->portals = (mportal_t *)p;
2414 }
2415
2416 /*
2417 =============
2418 RemovePortalFromNode
2419 =============
2420 */
2421 static void RemovePortalFromNodes(portal_t *portal)
2422 {
2423         int i;
2424         mnode_t *node;
2425         void **portalpointer;
2426         portal_t *t;
2427         for (i = 0;i < 2;i++)
2428         {
2429                 node = portal->nodes[i];
2430
2431                 portalpointer = (void **) &node->portals;
2432                 while (1)
2433                 {
2434                         t = *portalpointer;
2435                         if (!t)
2436                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2437
2438                         if (t == portal)
2439                         {
2440                                 if (portal->nodes[0] == node)
2441                                 {
2442                                         *portalpointer = portal->next[0];
2443                                         portal->nodes[0] = NULL;
2444                                 }
2445                                 else if (portal->nodes[1] == node)
2446                                 {
2447                                         *portalpointer = portal->next[1];
2448                                         portal->nodes[1] = NULL;
2449                                 }
2450                                 else
2451                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2452                                 break;
2453                         }
2454
2455                         if (t->nodes[0] == node)
2456                                 portalpointer = (void **) &t->next[0];
2457                         else if (t->nodes[1] == node)
2458                                 portalpointer = (void **) &t->next[1];
2459                         else
2460                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2461                 }
2462         }
2463 }
2464
2465 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2466 {
2467         int i, side;
2468         mnode_t *front, *back, *other_node;
2469         mplane_t clipplane, *plane;
2470         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2471         int numfrontpoints, numbackpoints;
2472         double frontpoints[3*MAX_PORTALPOINTS], backpoints[3*MAX_PORTALPOINTS];
2473
2474         // if a leaf, we're done
2475         if (node->contents)
2476                 return;
2477
2478         plane = node->plane;
2479
2480         front = node->children[0];
2481         back = node->children[1];
2482         if (front == back)
2483                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2484
2485         // create the new portal by generating a polygon for the node plane,
2486         // and clipping it by all of the other portals(which came from nodes above this one)
2487         nodeportal = AllocPortal();
2488         nodeportal->plane = *plane;
2489
2490         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);
2491         nodeportal->numpoints = 4;
2492         side = 0;       // shut up compiler warning
2493         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2494         {
2495                 clipplane = portal->plane;
2496                 if (portal->nodes[0] == portal->nodes[1])
2497                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2498                 if (portal->nodes[0] == node)
2499                         side = 0;
2500                 else if (portal->nodes[1] == node)
2501                 {
2502                         clipplane.dist = -clipplane.dist;
2503                         VectorNegate(clipplane.normal, clipplane.normal);
2504                         side = 1;
2505                 }
2506                 else
2507                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2508
2509                 for (i = 0;i < nodeportal->numpoints*3;i++)
2510                         frontpoints[i] = nodeportal->points[i];
2511                 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);
2512                 if (nodeportal->numpoints <= 0 || nodeportal->numpoints >= MAX_PORTALPOINTS)
2513                         break;
2514         }
2515
2516         if (nodeportal->numpoints < 3)
2517         {
2518                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2519                 nodeportal->numpoints = 0;
2520         }
2521         else if (nodeportal->numpoints >= MAX_PORTALPOINTS)
2522         {
2523                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal has too many points\n");
2524                 nodeportal->numpoints = 0;
2525         }
2526         else
2527         {
2528                 AddPortalToNodes(nodeportal, front, back);
2529
2530                 // split the portals of this node along this node's plane and assign them to the children of this node
2531                 // (migrating the portals downward through the tree)
2532                 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2533                 {
2534                         if (portal->nodes[0] == portal->nodes[1])
2535                                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2536                         if (portal->nodes[0] == node)
2537                                 side = 0;
2538                         else if (portal->nodes[1] == node)
2539                                 side = 1;
2540                         else
2541                                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2542                         nextportal = portal->next[side];
2543
2544                         other_node = portal->nodes[!side];
2545                         RemovePortalFromNodes(portal);
2546
2547                         // cut the portal into two portals, one on each side of the node plane
2548                         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); 
2549
2550                         if (!numfrontpoints)
2551                         {
2552                                 if (side == 0)
2553                                         AddPortalToNodes(portal, back, other_node);
2554                                 else
2555                                         AddPortalToNodes(portal, other_node, back);
2556                                 continue;
2557                         }
2558                         if (!numbackpoints)
2559                         {
2560                                 if (side == 0)
2561                                         AddPortalToNodes(portal, front, other_node);
2562                                 else
2563                                         AddPortalToNodes(portal, other_node, front);
2564                                 continue;
2565                         }
2566
2567                         // the portal is split
2568                         splitportal = AllocPortal();
2569                         temp = splitportal->chain;
2570                         *splitportal = *portal;
2571                         splitportal->chain = temp;
2572                         for (i = 0;i < numbackpoints*3;i++)
2573                                 splitportal->points[i] = backpoints[i];
2574                         splitportal->numpoints = numbackpoints;
2575                         for (i = 0;i < numfrontpoints*3;i++)
2576                                 portal->points[i] = frontpoints[i];
2577                         portal->numpoints = numfrontpoints;
2578
2579                         if (side == 0)
2580                         {
2581                                 AddPortalToNodes(portal, front, other_node);
2582                                 AddPortalToNodes(splitportal, back, other_node);
2583                         }
2584                         else
2585                         {
2586                                 AddPortalToNodes(portal, other_node, front);
2587                                 AddPortalToNodes(splitportal, other_node, back);
2588                         }
2589                 }
2590         }
2591
2592         Mod_Q1BSP_RecursiveNodePortals(front);
2593         Mod_Q1BSP_RecursiveNodePortals(back);
2594 }
2595
2596 static void Mod_Q1BSP_MakePortals(void)
2597 {
2598         portalchain = NULL;
2599         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2600         Mod_Q1BSP_FinalizePortals();
2601 }
2602
2603 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2604 {
2605 #if 0
2606         int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2607         msurface_t *surf, *s;
2608         float *v0, *v1, *v2, *v3;
2609         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2610                 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2611         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2612         {
2613                 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)
2614                 {
2615                         if (surf->neighborsurfaces[vertnum])
2616                                 continue;
2617                         surf->neighborsurfaces[vertnum] = NULL;
2618                         for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2619                         {
2620                                 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2621                                  || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2622                                  || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2623                                  || s == surf)
2624                                         continue;
2625                                 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2626                                         if (s->neighborsurfaces[vnum] == surf)
2627                                                 break;
2628                                 if (vnum < s->poly_numverts)
2629                                         continue;
2630                                 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)
2631                                 {
2632                                         if (s->neighborsurfaces[vnum] == NULL
2633                                          && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2634                                           || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2635                                         {
2636                                                 surf->neighborsurfaces[vertnum] = s;
2637                                                 s->neighborsurfaces[vnum] = surf;
2638                                                 break;
2639                                         }
2640                                 }
2641                                 if (vnum < s->poly_numverts)
2642                                         break;
2643                         }
2644                 }
2645         }
2646 #endif
2647 }
2648
2649 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2650 {
2651         int i, j, stylecounts[256], totalcount, remapstyles[256];
2652         msurface_t *surf;
2653         memset(stylecounts, 0, sizeof(stylecounts));
2654         for (i = 0;i < model->nummodelsurfaces;i++)
2655         {
2656                 surf = model->brushq1.surfaces + model->firstmodelsurface + i;
2657                 for (j = 0;j < MAXLIGHTMAPS;j++)
2658                         stylecounts[surf->styles[j]]++;
2659         }
2660         totalcount = 0;
2661         model->brushq1.light_styles = 0;
2662         for (i = 0;i < 255;i++)
2663         {
2664                 if (stylecounts[i])
2665                 {
2666                         remapstyles[i] = model->brushq1.light_styles++;
2667                         totalcount += stylecounts[i] + 1;
2668                 }
2669         }
2670         if (!totalcount)
2671                 return;
2672         model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2673         model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2674         model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2675         model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2676         model->brushq1.light_styles = 0;
2677         for (i = 0;i < 255;i++)
2678                 if (stylecounts[i])
2679                         model->brushq1.light_style[model->brushq1.light_styles++] = i;
2680         j = 0;
2681         for (i = 0;i < model->brushq1.light_styles;i++)
2682         {
2683                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2684                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2685         }
2686         for (i = 0;i < model->nummodelsurfaces;i++)
2687         {
2688                 surf = model->brushq1.surfaces + model->firstmodelsurface + i;
2689                 for (j = 0;j < MAXLIGHTMAPS;j++)
2690                         if (surf->styles[j] != 255)
2691                                 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2692         }
2693         j = 0;
2694         for (i = 0;i < model->brushq1.light_styles;i++)
2695         {
2696                 *model->brushq1.light_styleupdatechains[i] = NULL;
2697                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2698                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2699         }
2700 }
2701
2702 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2703 {
2704         int i, j;
2705         for (i = 0;i < model->brushq1.numtextures;i++)
2706                 model->brushq1.pvstexturechainslength[i] = 0;
2707         for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
2708         {
2709                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2710                 {
2711                         model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2712                         model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2713                 }
2714         }
2715         for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2716         {
2717                 if (model->brushq1.pvstexturechainslength[i])
2718                 {
2719                         model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2720                         j += model->brushq1.pvstexturechainslength[i] + 1;
2721                 }
2722                 else
2723                         model->brushq1.pvstexturechains[i] = NULL;
2724         }
2725         for (i = 0, j = model->firstmodelsurface;i < model->nummodelsurfaces;i++, j++)
2726                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2727                         *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2728         for (i = 0;i < model->brushq1.numtextures;i++)
2729         {
2730                 if (model->brushq1.pvstexturechainslength[i])
2731                 {
2732                         *model->brushq1.pvstexturechains[i] = NULL;
2733                         model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2734                 }
2735         }
2736 }
2737
2738 //Returns PVS data for a given point
2739 //(note: can return NULL)
2740 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2741 {
2742         mnode_t *node;
2743         Mod_CheckLoaded(model);
2744         node = model->brushq1.nodes;
2745         while (node->plane)
2746                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2747         if (((mleaf_t *)node)->clusterindex >= 0)
2748                 return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2749         else
2750                 return NULL;
2751 }
2752
2753 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2754 {
2755         while (node->plane)
2756         {
2757                 float d = PlaneDiff(org, node->plane);
2758                 if (d > radius)
2759                         node = node->children[0];
2760                 else if (d < -radius)
2761                         node = node->children[1];
2762                 else
2763                 {
2764                         // go down both sides
2765                         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2766                         node = node->children[1];
2767                 }
2768         }
2769         // if this leaf is in a cluster, accumulate the pvs bits
2770         if (((mleaf_t *)node)->clusterindex >= 0)
2771         {
2772                 int i;
2773                 qbyte *pvs = model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2774                 for (i = 0;i < pvsbytes;i++)
2775                         pvsbuffer[i] |= pvs[i];
2776         }
2777 }
2778
2779 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2780 //of the given point.
2781 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2782 {
2783         int bytes = ((model->brushq1.num_leafs - 1) + 7) >> 3;
2784         bytes = min(bytes, pvsbufferlength);
2785         if (r_novis.integer || !Mod_Q1BSP_GetPVS(model, org))
2786         {
2787                 memset(pvsbuffer, 0xFF, bytes);
2788                 return bytes;
2789         }
2790         memset(pvsbuffer, 0, bytes);
2791         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2792         return bytes;
2793 }
2794
2795 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2796 {
2797         vec3_t size;
2798         const hull_t *hull;
2799
2800         VectorSubtract(inmaxs, inmins, size);
2801         if (cmodel->brush.ishlbsp)
2802         {
2803                 if (size[0] < 3)
2804                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2805                 else if (size[0] <= 32)
2806                 {
2807                         if (size[2] < 54) // pick the nearest of 36 or 72
2808                                 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2809                         else
2810                                 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2811                 }
2812                 else
2813                         hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2814         }
2815         else
2816         {
2817                 if (size[0] < 3)
2818                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2819                 else if (size[0] <= 32)
2820                         hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2821                 else
2822                         hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2823         }
2824         VectorCopy(inmins, outmins);
2825         VectorAdd(inmins, hull->clip_size, outmaxs);
2826 }
2827
2828 /*
2829 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)
2830 {
2831         mleaf_t *leaf;
2832         for (;;)
2833         {
2834                 if (!BoxesOverlap(node->mins, node->maxs, mins, maxs))
2835                         return;
2836                 if (!node->plane)
2837                         break;
2838                 Mod_Q1BSP_RecursiveGetVisible(node->children[0], model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
2839                 node = node->children[1];
2840         }
2841         leaf = (mleaf_t *)node;
2842         if ((pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
2843         {
2844                 int marksurfacenum;
2845                 msurface_t *surf;
2846                 if (maxleafs && *numleafs < maxleafs)
2847                         leaflist[(*numleafs)++] = leaf;
2848                 if (maxsurfaces)
2849                 {
2850                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
2851                         {
2852                                 surf = model->brushq1.surfaces + leaf->firstmarksurface[marksurfacenum];
2853                                 if (surf->shadowmark != shadowmarkcount)
2854                                 {
2855                                         surf->shadowmark = shadowmarkcount;
2856                                         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)
2857                                                 surfacelist[(*numsurfaces)++] = surf;
2858                                 }
2859                         }
2860                 }
2861         }
2862 }
2863
2864 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)
2865 {
2866         // FIXME: support portals
2867         if (maxsurfaces)
2868                 *numsurfaces = 0;
2869         if (maxleafs)
2870                 *numleafs = 0;
2871         pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
2872         Mod_Q1BSP_RecursiveGetVisible(ent->model->brushq1.nodes + ent->model->brushq1.firstclipnode, model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces);
2873 }
2874 */
2875
2876 extern void R_Q1BSP_DrawSky(entity_render_t *ent);
2877 extern void R_Q1BSP_Draw(entity_render_t *ent);
2878 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);
2879 extern void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
2880 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);
2881 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2882 {
2883         int i, j, k;
2884         dheader_t *header;
2885         dmodel_t *bm;
2886         mempool_t *mainmempool;
2887         float dist, modelyawradius, modelradius, *vec;
2888         msurface_t *surf;
2889         int numshadowmeshtriangles;
2890
2891         mod->type = mod_brush;
2892
2893         header = (dheader_t *)buffer;
2894
2895         i = LittleLong(header->version);
2896         if (i != BSPVERSION && i != 30)
2897                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2898         mod->brush.ishlbsp = i == 30;
2899
2900         mod->soundfromcenter = true;
2901         mod->TraceBox = Mod_Q1BSP_TraceBox;
2902         mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2903         mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2904         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2905         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2906         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2907         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2908         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2909         mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2910         mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2911         mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2912         mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2913
2914         if (loadmodel->isworldmodel)
2915         {
2916                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2917                 // until we get a texture for it...
2918                 R_ResetQuakeSky();
2919         }
2920
2921 // swap all the lumps
2922         mod_base = (qbyte *)header;
2923
2924         for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2925                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2926
2927 // load into heap
2928
2929         // store which lightmap format to use
2930         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2931
2932         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2933         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2934         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2935         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2936         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2937         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2938         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2939         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2940         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2941         Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2942         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2943         // load submodels before leafs because they contain the number of vis leafs
2944         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2945         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2946         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2947         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2948
2949         if (!mod->brushq1.lightdata)
2950                 mod->brush.LightPoint = NULL;
2951
2952         if (mod->brushq1.data_compressedpvs)
2953                 Mem_Free(mod->brushq1.data_compressedpvs);
2954         mod->brushq1.data_compressedpvs = NULL;
2955         mod->brushq1.num_compressedpvs = 0;
2956
2957         Mod_Q1BSP_MakeHull0();
2958         Mod_Q1BSP_MakePortals();
2959
2960         mod->numframes = 2;             // regular and alternate animation
2961
2962         mainmempool = mod->mempool;
2963
2964         Mod_Q1BSP_LoadLightList();
2965         loadmodel = loadmodel;
2966
2967         // make a single combined shadow mesh to allow optimized shadow volume creation
2968         numshadowmeshtriangles = 0;
2969         for (j = 0, surf = loadmodel->brushq1.surfaces;j < loadmodel->brushq1.numsurfaces;j++, surf++)
2970         {
2971                 surf->num_firstshadowmeshtriangle = numshadowmeshtriangles;
2972                 numshadowmeshtriangles += surf->mesh.num_triangles;
2973         }
2974         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
2975         for (j = 0, surf = loadmodel->brushq1.surfaces;j < loadmodel->brushq1.numsurfaces;j++, surf++)
2976                 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);
2977         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
2978         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
2979         
2980         // LordHavoc: to clear the fog around the original quake submodel code, I
2981         // will explain:
2982         // first of all, some background info on the submodels:
2983         // model 0 is the map model (the world, named maps/e1m1.bsp for example)
2984         // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
2985         // now the weird for loop itself:
2986         // the loop functions in an odd way, on each iteration it sets up the
2987         // current 'mod' model (which despite the confusing code IS the model of
2988         // the number i), at the end of the loop it duplicates the model to become
2989         // the next submodel, and loops back to set up the new submodel.
2990
2991         // LordHavoc: now the explanation of my sane way (which works identically):
2992         // set up the world model, then on each submodel copy from the world model
2993         // and set up the submodel with the respective model info.
2994         for (i = 0;i < mod->brush.numsubmodels;i++)
2995         {
2996                 // LordHavoc: this code was originally at the end of this loop, but
2997                 // has been transformed to something more readable at the start here.
2998
2999                 // LordHavoc: only register submodels if it is the world
3000                 // (prevents external bsp models from replacing world submodels with
3001                 //  their own)
3002                 if (loadmodel->isworldmodel && i)
3003                 {
3004                         char name[10];
3005                         // duplicate the basic information
3006                         sprintf(name, "*%i", i);
3007                         mod = Mod_FindName(name);
3008                         // copy the base model to this one
3009                         *mod = *loadmodel;
3010                         // rename the clone back to its proper name
3011                         strcpy(mod->name, name);
3012                         // textures and memory belong to the main model
3013                         mod->texturepool = NULL;
3014                         mod->mempool = NULL;
3015                 }
3016
3017                 bm = &mod->brushq1.submodels[i];
3018
3019                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3020                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3021                 {
3022                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3023                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3024                 }
3025
3026                 mod->firstmodelsurface = bm->firstface;
3027                 mod->nummodelsurfaces = bm->numfaces;
3028
3029                 // make the model surface list (used by shadowing/lighting)
3030                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
3031                 for (j = 0;j < mod->nummodelsurfaces;j++)
3032                         mod->surfacelist[j] = mod->firstmodelsurface + j;
3033
3034                 // this gets altered below if sky is used
3035                 mod->DrawSky = NULL;
3036                 mod->Draw = R_Q1BSP_Draw;
3037                 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3038                 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3039                 mod->DrawLight = R_Q1BSP_DrawLight;
3040                 if (i != 0)
3041                 {
3042                         mod->brush.GetPVS = NULL;
3043                         mod->brush.FatPVS = NULL;
3044                         mod->brush.BoxTouchingPVS = NULL;
3045                         mod->brush.LightPoint = NULL;
3046                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3047                 }
3048                 mod->brushq1.pvstexturechains = Mem_Alloc(loadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
3049                 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(loadmodel->mempool,(mod->nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
3050                 mod->brushq1.pvstexturechainslength = Mem_Alloc(loadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
3051                 Mod_Q1BSP_BuildPVSTextureChains(mod);
3052                 Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
3053                 if (mod->nummodelsurfaces)
3054                 {
3055                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3056                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3057                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3058                         modelyawradius = 0;
3059                         modelradius = 0;
3060                         for (j = 0, surf = &mod->brushq1.surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
3061                         {
3062                                 // we only need to have a drawsky function if it is used(usually only on world model)
3063                                 if (surf->texinfo->texture->flags & SURF_DRAWSKY)
3064                                         mod->DrawSky = R_Q1BSP_DrawSky;
3065                                 // LordHavoc: submodels always clip, even if water
3066                                 if (mod->brush.numsubmodels - 1)
3067                                         surf->flags |= SURF_SOLIDCLIP;
3068                                 // calculate bounding shapes
3069                                 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
3070                                 {
3071                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3072                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3073                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3074                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3075                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3076                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3077                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3078                                         if (modelyawradius < dist)
3079                                                 modelyawradius = dist;
3080                                         dist += vec[2]*vec[2];
3081                                         if (modelradius < dist)
3082                                                 modelradius = dist;
3083                                 }
3084                         }
3085                         modelyawradius = sqrt(modelyawradius);
3086                         modelradius = sqrt(modelradius);
3087                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3088                         mod->yawmins[2] = mod->normalmins[2];
3089                         mod->yawmaxs[2] = mod->normalmaxs[2];
3090                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3091                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3092                         mod->radius = modelradius;
3093                         mod->radius2 = modelradius * modelradius;
3094                 }
3095                 else
3096                 {
3097                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3098                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3099                 }
3100                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->firstmodelsurface, mod->nummodelsurfaces, loadmodel->mempool);
3101
3102                 mod->brushq1.num_visleafs = bm->visleafs;
3103         }
3104
3105         //Mod_Q1BSP_ProcessLightList();
3106
3107         if (developer.integer)
3108                 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);
3109 }
3110
3111 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3112 {
3113 }
3114
3115 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3116 {
3117 /*
3118         d_t *in;
3119         m_t *out;
3120         int i, count;
3121
3122         in = (void *)(mod_base + l->fileofs);
3123         if (l->filelen % sizeof(*in))
3124                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3125         count = l->filelen / sizeof(*in);
3126         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3127
3128         loadmodel-> = out;
3129         loadmodel->num = count;
3130
3131         for (i = 0;i < count;i++, in++, out++)
3132         {
3133         }
3134 */
3135 }
3136
3137 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3138 {
3139 /*
3140         d_t *in;
3141         m_t *out;
3142         int i, count;
3143
3144         in = (void *)(mod_base + l->fileofs);
3145         if (l->filelen % sizeof(*in))
3146                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3147         count = l->filelen / sizeof(*in);
3148         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3149
3150         loadmodel-> = out;
3151         loadmodel->num = count;
3152
3153         for (i = 0;i < count;i++, in++, out++)
3154         {
3155         }
3156 */
3157 }
3158
3159 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3160 {
3161 /*
3162         d_t *in;
3163         m_t *out;
3164         int i, count;
3165
3166         in = (void *)(mod_base + l->fileofs);
3167         if (l->filelen % sizeof(*in))
3168                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3169         count = l->filelen / sizeof(*in);
3170         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3171
3172         loadmodel-> = out;
3173         loadmodel->num = count;
3174
3175         for (i = 0;i < count;i++, in++, out++)
3176         {
3177         }
3178 */
3179 }
3180
3181 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3182 {
3183 /*
3184         d_t *in;
3185         m_t *out;
3186         int i, count;
3187
3188         in = (void *)(mod_base + l->fileofs);
3189         if (l->filelen % sizeof(*in))
3190                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3191         count = l->filelen / sizeof(*in);
3192         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3193
3194         loadmodel-> = out;
3195         loadmodel->num = count;
3196
3197         for (i = 0;i < count;i++, in++, out++)
3198         {
3199         }
3200 */
3201 }
3202
3203 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3204 {
3205 /*
3206         d_t *in;
3207         m_t *out;
3208         int i, count;
3209
3210         in = (void *)(mod_base + l->fileofs);
3211         if (l->filelen % sizeof(*in))
3212                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3213         count = l->filelen / sizeof(*in);
3214         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3215
3216         loadmodel-> = out;
3217         loadmodel->num = count;
3218
3219         for (i = 0;i < count;i++, in++, out++)
3220         {
3221         }
3222 */
3223 }
3224
3225 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3226 {
3227 /*
3228         d_t *in;
3229         m_t *out;
3230         int i, count;
3231
3232         in = (void *)(mod_base + l->fileofs);
3233         if (l->filelen % sizeof(*in))
3234                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3235         count = l->filelen / sizeof(*in);
3236         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3237
3238         loadmodel-> = out;
3239         loadmodel->num = count;
3240
3241         for (i = 0;i < count;i++, in++, out++)
3242         {
3243         }
3244 */
3245 }
3246
3247 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3248 {
3249 /*
3250         d_t *in;
3251         m_t *out;
3252         int i, count;
3253
3254         in = (void *)(mod_base + l->fileofs);
3255         if (l->filelen % sizeof(*in))
3256                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3257         count = l->filelen / sizeof(*in);
3258         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3259
3260         loadmodel-> = out;
3261         loadmodel->num = count;
3262
3263         for (i = 0;i < count;i++, in++, out++)
3264         {
3265         }
3266 */
3267 }
3268
3269 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3270 {
3271 /*
3272         d_t *in;
3273         m_t *out;
3274         int i, count;
3275
3276         in = (void *)(mod_base + l->fileofs);
3277         if (l->filelen % sizeof(*in))
3278                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3279         count = l->filelen / sizeof(*in);
3280         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3281
3282         loadmodel-> = out;
3283         loadmodel->num = count;
3284
3285         for (i = 0;i < count;i++, in++, out++)
3286         {
3287         }
3288 */
3289 }
3290
3291 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3292 {
3293 /*
3294         d_t *in;
3295         m_t *out;
3296         int i, count;
3297
3298         in = (void *)(mod_base + l->fileofs);
3299         if (l->filelen % sizeof(*in))
3300                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3301         count = l->filelen / sizeof(*in);
3302         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3303
3304         loadmodel-> = out;
3305         loadmodel->num = count;
3306
3307         for (i = 0;i < count;i++, in++, out++)
3308         {
3309         }
3310 */
3311 }
3312
3313 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3314 {
3315 /*
3316         d_t *in;
3317         m_t *out;
3318         int i, count;
3319
3320         in = (void *)(mod_base + l->fileofs);
3321         if (l->filelen % sizeof(*in))
3322                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3323         count = l->filelen / sizeof(*in);
3324         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3325
3326         loadmodel-> = out;
3327         loadmodel->num = count;
3328
3329         for (i = 0;i < count;i++, in++, out++)
3330         {
3331         }
3332 */
3333 }
3334
3335 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3336 {
3337 /*
3338         d_t *in;
3339         m_t *out;
3340         int i, count;
3341
3342         in = (void *)(mod_base + l->fileofs);
3343         if (l->filelen % sizeof(*in))
3344                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3345         count = l->filelen / sizeof(*in);
3346         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3347
3348         loadmodel-> = out;
3349         loadmodel->num = count;
3350
3351         for (i = 0;i < count;i++, in++, out++)
3352         {
3353         }
3354 */
3355 }
3356
3357 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3358 {
3359 /*
3360         d_t *in;
3361         m_t *out;
3362         int i, count;
3363
3364         in = (void *)(mod_base + l->fileofs);
3365         if (l->filelen % sizeof(*in))
3366                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3367         count = l->filelen / sizeof(*in);
3368         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3369
3370         loadmodel-> = out;
3371         loadmodel->num = count;
3372
3373         for (i = 0;i < count;i++, in++, out++)
3374         {
3375         }
3376 */
3377 }
3378
3379 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3380 {
3381 /*
3382         d_t *in;
3383         m_t *out;
3384         int i, count;
3385
3386         in = (void *)(mod_base + l->fileofs);
3387         if (l->filelen % sizeof(*in))
3388                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3389         count = l->filelen / sizeof(*in);
3390         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3391
3392         loadmodel-> = out;
3393         loadmodel->num = count;
3394
3395         for (i = 0;i < count;i++, in++, out++)
3396         {
3397         }
3398 */
3399 }
3400
3401 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3402 {
3403 /*
3404         d_t *in;
3405         m_t *out;
3406         int i, count;
3407
3408         in = (void *)(mod_base + l->fileofs);
3409         if (l->filelen % sizeof(*in))
3410                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3411         count = l->filelen / sizeof(*in);
3412         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3413
3414         loadmodel-> = out;
3415         loadmodel->num = count;
3416
3417         for (i = 0;i < count;i++, in++, out++)
3418         {
3419         }
3420 */
3421 }
3422
3423 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3424 {
3425 /*
3426         d_t *in;
3427         m_t *out;
3428         int i, count;
3429
3430         in = (void *)(mod_base + l->fileofs);
3431         if (l->filelen % sizeof(*in))
3432                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3433         count = l->filelen / sizeof(*in);
3434         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3435
3436         loadmodel-> = out;
3437         loadmodel->num = count;
3438
3439         for (i = 0;i < count;i++, in++, out++)
3440         {
3441         }
3442 */
3443 }
3444
3445 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3446 {
3447 /*
3448         d_t *in;
3449         m_t *out;
3450         int i, count;
3451
3452         in = (void *)(mod_base + l->fileofs);
3453         if (l->filelen % sizeof(*in))
3454                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3455         count = l->filelen / sizeof(*in);
3456         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3457
3458         loadmodel-> = out;
3459         loadmodel->num = count;
3460
3461         for (i = 0;i < count;i++, in++, out++)
3462         {
3463         }
3464 */
3465 }
3466
3467 static void Mod_Q2BSP_LoadModels(lump_t *l)
3468 {
3469 /*
3470         d_t *in;
3471         m_t *out;
3472         int i, count;
3473
3474         in = (void *)(mod_base + l->fileofs);
3475         if (l->filelen % sizeof(*in))
3476                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3477         count = l->filelen / sizeof(*in);
3478         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3479
3480         loadmodel-> = out;
3481         loadmodel->num = count;
3482
3483         for (i = 0;i < count;i++, in++, out++)
3484         {
3485         }
3486 */
3487 }
3488
3489 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3490 {
3491         int i;
3492         q2dheader_t *header;
3493
3494         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3495
3496         mod->type = mod_brushq2;
3497
3498         header = (q2dheader_t *)buffer;
3499
3500         i = LittleLong(header->version);
3501         if (i != Q2BSPVERSION)
3502                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3503         mod->brush.ishlbsp = false;
3504         if (loadmodel->isworldmodel)
3505         {
3506                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3507                 // until we get a texture for it...
3508                 R_ResetQuakeSky();
3509         }
3510
3511         mod_base = (qbyte *)header;
3512
3513         // swap all the lumps
3514         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3515                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3516
3517         // store which lightmap format to use
3518         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3519
3520         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3521         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3522         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3523         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3524         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3525         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3526         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3527         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3528         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3529         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3530         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3531         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3532         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3533         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3534         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3535         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3536         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3537         // LordHavoc: must go last because this makes the submodels
3538         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3539 }
3540
3541 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3542 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3543
3544 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3545 {
3546         const char *data;
3547         char key[128], value[4096];
3548         float v[3];
3549         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3550         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3551         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3552         if (!l->filelen)
3553                 return;
3554         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3555         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3556         data = loadmodel->brush.entities;
3557         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3558         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3559         {
3560                 while (1)
3561                 {
3562                         if (!COM_ParseToken(&data, false))
3563                                 break; // error
3564                         if (com_token[0] == '}')
3565                                 break; // end of worldspawn
3566                         if (com_token[0] == '_')
3567                                 strcpy(key, com_token + 1);
3568                         else
3569                                 strcpy(key, com_token);
3570                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3571                                 key[strlen(key)-1] = 0;
3572                         if (!COM_ParseToken(&data, false))
3573                                 break; // error
3574                         strcpy(value, com_token);
3575                         if (!strcmp("gridsize", key))
3576                         {
3577                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3578                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3579                         }
3580                 }
3581         }
3582 }
3583
3584 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3585 {
3586         q3dtexture_t *in;
3587         q3mtexture_t *out;
3588         int i, count;
3589         int j, c;
3590         fssearch_t *search;
3591         char *f;
3592         const char *text;
3593         int flags;
3594         char shadername[Q3PATHLENGTH];
3595         char sky[Q3PATHLENGTH];
3596
3597         in = (void *)(mod_base + l->fileofs);
3598         if (l->filelen % sizeof(*in))
3599                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3600         count = l->filelen / sizeof(*in);
3601         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3602
3603         loadmodel->brushq3.data_textures = out;
3604         loadmodel->brushq3.num_textures = count;
3605
3606         for (i = 0;i < count;i++, in++, out++)
3607         {
3608                 out->number = i;
3609                 strlcpy (out->name, in->name, sizeof (out->name));
3610                 out->surfaceflags = LittleLong(in->surfaceflags);
3611                 out->nativecontents = LittleLong(in->contents);
3612                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3613                 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true, true);
3614                 out->surfaceparms = -1;
3615         }
3616
3617         // do a quick parse of shader files to get surfaceparms
3618         if ((search = FS_Search("scripts/*.shader", true, false)))
3619         {
3620                 for (i = 0;i < search->numfilenames;i++)
3621                 {
3622                         if ((f = FS_LoadFile(search->filenames[i], tempmempool, false)))
3623                         {
3624                                 text = f;
3625                                 while (COM_ParseToken(&text, false))
3626                                 {
3627                                         strlcpy (shadername, com_token, sizeof (shadername));
3628                                         flags = 0;
3629                                         sky[0] = 0;
3630                                         if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{"))
3631                                         {
3632                                                 while (COM_ParseToken(&text, false))
3633                                                 {
3634                                                         if (!strcasecmp(com_token, "}"))
3635                                                                 break;
3636                                                         else if (!strcasecmp(com_token, "{"))
3637                                                         {
3638                                                                 while (COM_ParseToken(&text, false))
3639                                                                 {
3640                                                                         if (!strcasecmp(com_token, "}"))
3641                                                                                 break;
3642                                                                 }
3643                                                         }
3644                                                         else if (!strcasecmp(com_token, "surfaceparm"))
3645                                                         {
3646                                                                 if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3647                                                                 {
3648                                                                         if (!strcasecmp(com_token, "alphashadow"))
3649                                                                                 flags |= Q3SURFACEPARM_ALPHASHADOW;
3650                                                                         else if (!strcasecmp(com_token, "areaportal"))
3651                                                                                 flags |= Q3SURFACEPARM_AREAPORTAL;
3652                                                                         else if (!strcasecmp(com_token, "clusterportal"))
3653                                                                                 flags |= Q3SURFACEPARM_CLUSTERPORTAL;
3654                                                                         else if (!strcasecmp(com_token, "detail"))
3655                                                                                 flags |= Q3SURFACEPARM_DETAIL;
3656                                                                         else if (!strcasecmp(com_token, "donotenter"))
3657                                                                                 flags |= Q3SURFACEPARM_DONOTENTER;
3658                                                                         else if (!strcasecmp(com_token, "fog"))
3659                                                                                 flags |= Q3SURFACEPARM_FOG;
3660                                                                         else if (!strcasecmp(com_token, "lava"))
3661                                                                                 flags |= Q3SURFACEPARM_LAVA;
3662                                                                         else if (!strcasecmp(com_token, "lightfilter"))
3663                                                                                 flags |= Q3SURFACEPARM_LIGHTFILTER;
3664                                                                         else if (!strcasecmp(com_token, "metalsteps"))
3665                                                                                 flags |= Q3SURFACEPARM_METALSTEPS;
3666                                                                         else if (!strcasecmp(com_token, "nodamage"))
3667                                                                                 flags |= Q3SURFACEPARM_NODAMAGE;
3668                                                                         else if (!strcasecmp(com_token, "nodlight"))
3669                                                                                 flags |= Q3SURFACEPARM_NODLIGHT;
3670                                                                         else if (!strcasecmp(com_token, "nodraw"))
3671                                                                                 flags |= Q3SURFACEPARM_NODRAW;
3672                                                                         else if (!strcasecmp(com_token, "nodrop"))
3673                                                                                 flags |= Q3SURFACEPARM_NODROP;
3674                                                                         else if (!strcasecmp(com_token, "noimpact"))
3675                                                                                 flags |= Q3SURFACEPARM_NOIMPACT;
3676                                                                         else if (!strcasecmp(com_token, "nolightmap"))
3677                                                                                 flags |= Q3SURFACEPARM_NOLIGHTMAP;
3678                                                                         else if (!strcasecmp(com_token, "nomarks"))
3679                                                                                 flags |= Q3SURFACEPARM_NOMARKS;
3680                                                                         else if (!strcasecmp(com_token, "nomipmaps"))
3681                                                                                 flags |= Q3SURFACEPARM_NOMIPMAPS;
3682                                                                         else if (!strcasecmp(com_token, "nonsolid"))
3683                                                                                 flags |= Q3SURFACEPARM_NONSOLID;
3684                                                                         else if (!strcasecmp(com_token, "origin"))
3685                                                                                 flags |= Q3SURFACEPARM_ORIGIN;
3686                                                                         else if (!strcasecmp(com_token, "playerclip"))
3687                                                                                 flags |= Q3SURFACEPARM_PLAYERCLIP;
3688                                                                         else if (!strcasecmp(com_token, "sky"))
3689                                                                                 flags |= Q3SURFACEPARM_SKY;
3690                                                                         else if (!strcasecmp(com_token, "slick"))
3691                                                                                 flags |= Q3SURFACEPARM_SLICK;
3692                                                                         else if (!strcasecmp(com_token, "slime"))
3693                                                                                 flags |= Q3SURFACEPARM_SLIME;
3694                                                                         else if (!strcasecmp(com_token, "structural"))
3695                                                                                 flags |= Q3SURFACEPARM_STRUCTURAL;
3696                                                                         else if (!strcasecmp(com_token, "trans"))
3697                                                                                 flags |= Q3SURFACEPARM_TRANS;
3698                                                                         else if (!strcasecmp(com_token, "water"))
3699                                                                                 flags |= Q3SURFACEPARM_WATER;
3700                                                                         else
3701                                                                                 Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token);
3702                                                                         if (!COM_ParseToken(&text, true) || strcasecmp(com_token, "\n"))
3703                                                                         {
3704                                                                                 Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]);
3705                                                                                 goto parseerror;
3706                                                                         }
3707                                                                 }
3708                                                                 else
3709                                                                 {
3710                                                                         Con_Printf("%s parsing error: surfaceparm expects a parameter.\n", search->filenames[i]);
3711                                                                         goto parseerror;
3712                                                                 }
3713                                                         }
3714                                                         else if (!strcasecmp(com_token, "sky"))
3715                                                         {
3716                                                                 if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3717                                                                         if (strlen(com_token) < sizeof(sky))
3718                                                                                 strcpy(sky, com_token);
3719                                                         }
3720                                                         else if (!strcasecmp(com_token, "skyparms"))
3721                                                         {
3722                                                                 if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3723                                                                 {
3724                                                                         if (strlen(com_token) < sizeof(sky) && !atoi(com_token) && strcasecmp(com_token, "-"))
3725                                                                                 strcpy(sky, com_token);
3726                                                                         if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n"))
3727                                                                                 COM_ParseToken(&text, true);
3728                                                                 }
3729                                                         }
3730                                                         else
3731                                                         {
3732                                                                 // look for linebreak or }
3733                                                                 while(COM_ParseToken(&text, true) && strcasecmp(com_token, "\n") && strcasecmp(com_token, "}"));
3734                                                                 // break out to top level if it was }
3735                                                                 if (!strcasecmp(com_token, "}"))
3736                                                                         break;
3737                                                         }
3738                                                 }
3739                                                 // add shader to list (shadername and flags)
3740                                                 // actually here we just poke into the texture settings
3741                                                 for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3742                                                 {
3743                                                         if (!strcasecmp(out->name, shadername))
3744                                                         {
3745                                                                 out->surfaceparms = flags;
3746                                                                 if ((flags & Q3SURFACEPARM_SKY) && sky[0])
3747                                                                 {
3748                                                                         // quake3 seems to append a _ to the skybox name, so this must do so as well
3749                                                                         snprintf(loadmodel->brush.skybox, sizeof(loadmodel->brush.skybox), "%s_", sky);
3750                                                                 }
3751                                                         }
3752                                                 }
3753                                         }
3754                                         else
3755                                         {
3756                                                 Con_Printf("%s parsing error - expected \"{\", found \"%s\"\n", search->filenames[i], com_token);
3757                                                 goto parseerror;
3758                                         }
3759                                 }
3760 parseerror:
3761                                 Mem_Free(f);
3762                         }
3763                 }
3764         }
3765
3766         c = 0;
3767         for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++)
3768         {
3769                 if (out->surfaceparms == -1)
3770                 {
3771                         c++;
3772                         Con_DPrintf("%s: No shader found for texture \"%s\"\n", loadmodel->name, out->name);
3773                         out->surfaceparms = 0;
3774                         // these are defaults
3775                         if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")
3776                          || !strcmp(out->name, "nodraw") || !strcmp(out->name, "common/nodraw") || !strcmp(out->name, "textures/common/nodraw"))
3777                                 out->surfaceparms |= Q3SURFACEPARM_NODRAW;
3778                         if (!strncmp(out->name, "textures/skies/", 15))
3779                                 out->surfaceparms |= Q3SURFACEPARM_SKY;
3780                         if (R_TextureHasAlpha(out->skin.base))
3781                                 out->surfaceparms |= Q3SURFACEPARM_TRANS;
3782                 }
3783         }
3784         Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
3785 }
3786
3787 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3788 {
3789         q3dplane_t *in;
3790         mplane_t *out;
3791         int i, count;
3792
3793         in = (void *)(mod_base + l->fileofs);
3794         if (l->filelen % sizeof(*in))
3795                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3796         count = l->filelen / sizeof(*in);
3797         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3798
3799         loadmodel->brushq3.data_planes = out;
3800         loadmodel->brushq3.num_planes = count;
3801
3802         for (i = 0;i < count;i++, in++, out++)
3803         {
3804                 out->normal[0] = LittleLong(in->normal[0]);
3805                 out->normal[1] = LittleLong(in->normal[1]);
3806                 out->normal[2] = LittleLong(in->normal[2]);
3807                 out->dist = LittleLong(in->dist);
3808                 PlaneClassify(out);
3809         }
3810 }
3811
3812 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3813 {
3814         q3dbrushside_t *in;
3815         q3mbrushside_t *out;
3816         int i, n, count;
3817
3818         in = (void *)(mod_base + l->fileofs);
3819         if (l->filelen % sizeof(*in))
3820                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3821         count = l->filelen / sizeof(*in);
3822         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3823
3824         loadmodel->brushq3.data_brushsides = out;
3825         loadmodel->brushq3.num_brushsides = count;
3826
3827         for (i = 0;i < count;i++, in++, out++)
3828         {
3829                 n = LittleLong(in->planeindex);
3830                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3831                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3832                 out->plane = loadmodel->brushq3.data_planes + n;
3833                 n = LittleLong(in->textureindex);
3834                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3835                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3836                 out->texture = loadmodel->brushq3.data_textures + n;
3837         }
3838 }
3839
3840 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3841 {
3842         q3dbrush_t *in;
3843         q3mbrush_t *out;
3844         int i, j, n, c, count, maxplanes;
3845         mplane_t *planes;
3846
3847         in = (void *)(mod_base + l->fileofs);
3848         if (l->filelen % sizeof(*in))
3849                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3850         count = l->filelen / sizeof(*in);
3851         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3852
3853         loadmodel->brushq3.data_brushes = out;
3854         loadmodel->brushq3.num_brushes = count;
3855
3856         maxplanes = 0;
3857         planes = NULL;
3858
3859         for (i = 0;i < count;i++, in++, out++)
3860         {
3861                 n = LittleLong(in->firstbrushside);
3862                 c = LittleLong(in->numbrushsides);
3863                 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3864                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3865                 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3866                 out->numbrushsides = c;
3867                 n = LittleLong(in->textureindex);
3868                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3869                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3870                 out->texture = loadmodel->brushq3.data_textures + n;
3871
3872                 // make a list of mplane_t structs to construct a colbrush from
3873                 if (maxplanes < out->numbrushsides)
3874                 {
3875                         maxplanes = out->numbrushsides;
3876                         if (planes)
3877                                 Mem_Free(planes);
3878                         planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3879                 }
3880                 for (j = 0;j < out->numbrushsides;j++)
3881                 {
3882                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3883                         planes[j].dist = out->firstbrushside[j].plane->dist;
3884                 }
3885                 // make the colbrush from the planes
3886                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3887         }
3888         if (planes)
3889                 Mem_Free(planes);
3890 }
3891
3892 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3893 {
3894         q3deffect_t *in;
3895         q3meffect_t *out;
3896         int i, n, count;
3897
3898         in = (void *)(mod_base + l->fileofs);
3899         if (l->filelen % sizeof(*in))
3900                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3901         count = l->filelen / sizeof(*in);
3902         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3903
3904         loadmodel->brushq3.data_effects = out;
3905         loadmodel->brushq3.num_effects = count;
3906
3907         for (i = 0;i < count;i++, in++, out++)
3908         {
3909                 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3910                 n = LittleLong(in->brushindex);
3911                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3912                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3913                 out->brush = loadmodel->brushq3.data_brushes + n;
3914                 out->unknown = LittleLong(in->unknown);
3915         }
3916 }
3917
3918 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3919 {
3920         q3dvertex_t *in;
3921         int i, count;
3922
3923         in = (void *)(mod_base + l->fileofs);
3924         if (l->filelen % sizeof(*in))
3925                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3926         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3927         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3928         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3929         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3930         loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3931         loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3932         loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3933         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3934
3935         for (i = 0;i < count;i++, in++)
3936         {
3937                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3938                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3939                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3940                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3941                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3942                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3943                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3944                 // svector/tvector are calculated later in face loading
3945                 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3946                 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3947                 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3948                 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3949                 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3950                 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3951                 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3952                 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3953                 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3954                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3955                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3956                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3957                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3958         }
3959 }
3960
3961 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3962 {
3963         int *in;
3964         int *out;
3965         int i, count;
3966
3967         in = (void *)(mod_base + l->fileofs);
3968         if (l->filelen % sizeof(int[3]))
3969                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3970         count = l->filelen / sizeof(*in);
3971         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3972
3973         loadmodel->brushq3.num_triangles = count / 3;
3974         loadmodel->brushq3.data_element3i = out;
3975         loadmodel->brushq3.data_neighbor3i = out + count;
3976
3977         for (i = 0;i < count;i++, in++, out++)
3978         {
3979                 *out = LittleLong(*in);
3980                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3981                 {
3982                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3983                         *out = 0;
3984                 }
3985         }
3986 }
3987
3988 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3989 {
3990         q3dlightmap_t *in;
3991         rtexture_t **out;
3992         int i, count;
3993
3994         if (!l->filelen)
3995                 return;
3996         in = (void *)(mod_base + l->fileofs);
3997         if (l->filelen % sizeof(*in))
3998                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3999         count = l->filelen / sizeof(*in);
4000         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4001
4002         loadmodel->brushq3.data_lightmaps = out;
4003         loadmodel->brushq3.num_lightmaps = count;
4004
4005         for (i = 0;i < count;i++, in++, out++)
4006                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
4007 }
4008
4009 static void Mod_Q3BSP_LoadFaces(lump_t *l)
4010 {
4011         q3dface_t *in;
4012         q3msurface_t *out;
4013         int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
4014         //int *originalelement3i;
4015         //int *originalneighbor3i;
4016         float *originalvertex3f;
4017         //float *originalsvector3f;
4018         //float *originaltvector3f;
4019         //float *originalnormal3f;
4020         float *originalcolor4f;
4021         float *originaltexcoordtexture2f;
4022         float *originaltexcoordlightmap2f;
4023         float *v;
4024
4025         in = (void *)(mod_base + l->fileofs);
4026         if (l->filelen % sizeof(*in))
4027                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
4028         count = l->filelen / sizeof(*in);
4029         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4030
4031         loadmodel->brushq3.data_faces = out;
4032         loadmodel->brushq3.num_faces = count;
4033
4034         for (i = 0;i < count;i++, in++, out++)
4035         {
4036                 // check face type first
4037                 out->type = LittleLong(in->type);
4038                 if (out->type != Q3FACETYPE_POLYGON
4039                  && out->type != Q3FACETYPE_PATCH
4040                  && out->type != Q3FACETYPE_MESH
4041                  && out->type != Q3FACETYPE_FLARE)
4042                 {
4043                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
4044                         out->num_vertices = 0;
4045                         out->num_triangles = 0;
4046                         out->type = 0; // error
4047                         continue;
4048                 }
4049
4050                 n = LittleLong(in->textureindex);
4051                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
4052                 {
4053                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
4054                         out->num_vertices = 0;
4055                         out->num_triangles = 0;
4056                         out->type = 0; // error
4057                         continue;
4058                         n = 0;
4059                 }
4060                 out->texture = loadmodel->brushq3.data_textures + n;
4061                 n = LittleLong(in->effectindex);
4062                 if (n < -1 || n >= loadmodel->brushq3.num_effects)
4063                 {
4064                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
4065                         n = -1;
4066                 }
4067                 if (n == -1)
4068                         out->effect = NULL;
4069                 else
4070                         out->effect = loadmodel->brushq3.data_effects + n;
4071                 n = LittleLong(in->lightmapindex);
4072                 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
4073                 {
4074                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
4075                         n = -1;
4076                 }
4077                 if (n == -1)
4078                         out->lightmaptexture = NULL;
4079                 else
4080                         out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
4081
4082                 out->firstvertex = LittleLong(in->firstvertex);
4083                 out->num_vertices = LittleLong(in->numvertices);
4084                 out->firstelement = LittleLong(in->firstelement);
4085                 out->num_triangles = LittleLong(in->numelements) / 3;
4086                 if (out->num_triangles * 3 != LittleLong(in->numelements))
4087                 {
4088                         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));
4089                         out->num_vertices = 0;
4090                         out->num_triangles = 0;
4091                         out->type = 0; // error
4092                         continue;
4093                 }
4094                 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
4095                 {
4096                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->num_vertices, loadmodel->brushq3.num_vertices);
4097                         out->num_vertices = 0;
4098                         out->num_triangles = 0;
4099                         out->type = 0; // error
4100                         continue;
4101                 }
4102                 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
4103                 {
4104                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->num_triangles * 3, loadmodel->brushq3.num_triangles * 3);
4105                         out->num_vertices = 0;
4106                         out->num_triangles = 0;
4107                         out->type = 0; // error
4108                         continue;
4109                 }
4110                 switch(out->type)
4111                 {
4112                 case Q3FACETYPE_POLYGON:
4113                 case Q3FACETYPE_MESH:
4114                         // no processing necessary
4115                         out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4116                         out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4117                         out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4118                         out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4119                         out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4120                         out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4121                         out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4122                         out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4123                         out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4124                         break;
4125                 case Q3FACETYPE_PATCH:
4126                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
4127                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
4128                         if (patchsize[0] < 1 || patchsize[1] < 1)
4129                         {
4130                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
4131                                 out->num_vertices = 0;
4132                                 out->num_triangles = 0;
4133                                 out->type = 0; // error
4134                                 continue;
4135                         }
4136                         originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4137                         //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4138                         //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4139                         //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4140                         originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4141                         originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4142                         originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4143                         //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
4144                         //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4145                         /*
4146                         originalvertex3f = out->data_vertex3f;
4147                         //originalsvector3f = out->data_svector3f;
4148                         //originaltvector3f = out->data_tvector3f;
4149                         //originalnormal3f = out->data_normal3f;
4150                         originalcolor4f = out->data_color4f;
4151                         originaltexcoordtexture2f = out->data_texcoordtexture2f;
4152                         originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
4153                         //originalelement3i = out->data_element3i;
4154                         //originalneighbor3i = out->data_neighbor3i;
4155                         */
4156                         // convert patch to Q3FACETYPE_MESH
4157                         xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
4158                         ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_tolerance.value, 10);
4159                         // bound to user settings
4160                         xlevel = bound(r_subdivisions_minlevel.integer, xlevel, r_subdivisions_maxlevel.integer);
4161                         ylevel = bound(r_subdivisions_minlevel.integer, ylevel, r_subdivisions_maxlevel.integer);
4162                         // bound to sanity settings
4163                         xlevel = bound(0, xlevel, 10);
4164                         ylevel = bound(0, ylevel, 10);
4165                         // bound to user limit on vertices
4166                         while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_maxvertices.integer, 262144))
4167                         {
4168                                 if (xlevel > ylevel)
4169                                         xlevel--;
4170                                 else
4171                                         ylevel--;
4172                         }
4173                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
4174                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
4175                         finalvertices = finalwidth * finalheight;
4176                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4177                         out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
4178                         out->data_svector3f = out->data_vertex3f + finalvertices * 3;
4179                         out->data_tvector3f = out->data_svector3f + finalvertices * 3;
4180                         out->data_normal3f = out->data_tvector3f + finalvertices * 3;
4181                         out->data_color4f = out->data_normal3f + finalvertices * 3;
4182                         out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
4183                         out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
4184                         out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
4185                         out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
4186                         out->type = Q3FACETYPE_MESH;
4187                         out->firstvertex = -1;
4188                         out->num_vertices = finalvertices;
4189                         out->firstelement = -1;
4190                         out->num_triangles = finaltriangles;
4191                         // generate geometry
4192                         // (note: normals are skipped because they get recalculated)
4193                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
4194                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
4195                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
4196                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
4197                         // generate elements
4198                         e = out->data_element3i;
4199                         for (y = 0;y < finalheight - 1;y++)
4200                         {
4201                                 row0 = (y + 0) * finalwidth;
4202                                 row1 = (y + 1) * finalwidth;
4203                                 for (x = 0;x < finalwidth - 1;x++)
4204                                 {
4205                                         *e++ = row0;
4206                                         *e++ = row1;
4207                                         *e++ = row0 + 1;
4208                                         *e++ = row1;
4209                                         *e++ = row1 + 1;
4210                                         *e++ = row0 + 1;
4211                                         row0++;
4212                                         row1++;
4213                                 }
4214                         }
4215                         out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
4216                         if (developer.integer)
4217                         {
4218                                 if (out->num_triangles < finaltriangles)
4219                                         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);
4220                                 else
4221                                         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);
4222                         }
4223                         // q3map does not put in collision brushes for curves... ugh
4224                         // build the lower quality collision geometry
4225                         out->collisions = true;
4226                         xlevel = QuadraticBSplinePatchSubdivisionLevelOnX(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
4227                         ylevel = QuadraticBSplinePatchSubdivisionLevelOnY(patchsize[0], patchsize[1], 3, originalvertex3f, r_subdivisions_collision_tolerance.value, 10);
4228                         // bound to user settings
4229                         xlevel = bound(r_subdivisions_collision_minlevel.integer, xlevel, r_subdivisions_collision_maxlevel.integer);
4230                         ylevel = bound(r_subdivisions_collision_minlevel.integer, ylevel, r_subdivisions_collision_maxlevel.integer);
4231                         // bound to sanity settings
4232                         xlevel = bound(0, xlevel, 10);
4233                         ylevel = bound(0, ylevel, 10);
4234                         // bound to user limit on vertices
4235                         while ((xlevel > 0 || ylevel > 0) && (((patchsize[0] - 1) << xlevel) + 1) * (((patchsize[1] - 1) << ylevel) + 1) > min(r_subdivisions_collision_maxvertices.integer, 262144))
4236                         {
4237                                 if (xlevel > ylevel)
4238                                         xlevel--;
4239                                 else
4240                                         ylevel--;
4241                         }
4242                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
4243                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
4244                         finalvertices = finalwidth * finalheight;
4245                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
4246                         out->data_collisionvertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * finalvertices);
4247                         out->data_collisionelement3i = Mem_Alloc(loadmodel->mempool, sizeof(int[3]) * finaltriangles);
4248                         out->num_collisionvertices = finalvertices;
4249                         out->num_collisiontriangles = finaltriangles;
4250                         QuadraticBSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_collisionvertex3f);
4251                         // generate elements
4252                         e = out->data_collisionelement3i;
4253                         for (y = 0;y < finalheight - 1;y++)
4254                         {
4255                                 row0 = (y + 0) * finalwidth;
4256                                 row1 = (y + 1) * finalwidth;
4257                                 for (x = 0;x < finalwidth - 1;x++)
4258                                 {
4259                                         *e++ = row0;
4260                                         *e++ = row1;
4261                                         *e++ = row0 + 1;
4262                                         *e++ = row1;
4263                                         *e++ = row1 + 1;
4264                                         *e++ = row0 + 1;
4265                                         row0++;
4266                                         row1++;
4267                                 }
4268                         }
4269                         out->num_collisiontriangles = Mod_RemoveDegenerateTriangles(out->num_collisiontriangles, out->data_collisionelement3i, out->data_collisionelement3i, out->data_collisionvertex3f);
4270                         if (developer.integer)
4271                         {
4272                                 if (out->num_collisiontriangles < finaltriangles)
4273                                         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);
4274                                 else
4275                                         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);
4276                         }
4277                         break;
4278                 case Q3FACETYPE_FLARE:
4279                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
4280                         // don't render it
4281                         out->num_vertices = 0;
4282                         out->num_triangles = 0;
4283                         out->type = 0;
4284                         break;
4285                 }
4286                 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
4287                         if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4288                                 invalidelements++;
4289                 if (invalidelements)
4290                 {
4291                         Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->num_vertices, out->firstelement, out->num_triangles * 3);
4292                         for (j = 0;j < out->num_triangles * 3;j++)
4293                         {
4294                                 Con_Printf(" %i", out->data_element3i[j]);
4295                                 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
4296                                         out->data_element3i[j] = 0;
4297                         }
4298                         Con_Print("\n");
4299                 }
4300                 // for shadow volumes
4301                 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
4302                 // for per pixel lighting
4303                 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);
4304                 // calculate a bounding box
4305                 VectorClear(out->mins);
4306                 VectorClear(out->maxs);
4307                 if (out->num_vertices)
4308                 {
4309                         VectorCopy(out->data_vertex3f, out->mins);
4310                         VectorCopy(out->data_vertex3f, out->maxs);
4311                         for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
4312                         {
4313                                 out->mins[0] = min(out->mins[0], v[0]);
4314                                 out->maxs[0] = max(out->maxs[0], v[0]);
4315                                 out->mins[1] = min(out->mins[1], v[1]);
4316                                 out->maxs[1] = max(out->maxs[1], v[1]);
4317                                 out->mins[2] = min(out->mins[2], v[2]);
4318                                 out->maxs[2] = max(out->maxs[2], v[2]);
4319                         }
4320                         out->mins[0] -= 1.0f;
4321                         out->mins[1] -= 1.0f;
4322                         out->mins[2] -= 1.0f;
4323                         out->maxs[0] += 1.0f;
4324                         out->maxs[1] += 1.0f;
4325                         out->maxs[2] += 1.0f;
4326                 }
4327         }
4328
4329         // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
4330         /*
4331         {
4332         int totalverts, totaltris;
4333         int originalnum_vertices;
4334         float *originaldata_vertex3f;
4335         float *originaldata_texcoordtexture2f;
4336         float *originaldata_texcoordlightmap2f;
4337         float *originaldata_svector3f;
4338         float *originaldata_tvector3f;
4339         float *originaldata_normal3f;
4340         float *originaldata_color4f;
4341         int originalnum_triangles;
4342         int *originaldata_element3i;
4343         int *originaldata_neighbor3i;
4344
4345         totalverts = 0;
4346         totaltris = 0;
4347         for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4348         {
4349                 if (!out->type)
4350                         continue;
4351                 totalverts += out->num_vertices;
4352                 totaltris += out->num_triangles;
4353         }
4354
4355         originalnum_vertices = loadmodel->brushq3.num_vertices;
4356         originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
4357         originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
4358         originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
4359         originaldata_svector3f = loadmodel->brushq3.data_svector3f;
4360         originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
4361         originaldata_normal3f = loadmodel->brushq3.data_normal3f;
4362         originaldata_color4f = loadmodel->brushq3.data_color4f;
4363         originalnum_triangles = loadmodel->brushq3.num_triangles;
4364         originaldata_element3i = loadmodel->brushq3.data_element3i;
4365         originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
4366         loadmodel->brushq3.num_vertices = totalverts;
4367         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
4368         loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
4369         loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
4370         loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
4371         loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
4372         loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
4373         loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
4374         loadmodel->brushq3.num_triangles = totaltris;
4375         loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
4376         loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
4377         totalverts = 0;
4378         totaltris = 0;
4379         for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
4380         {
4381                 if (!out->type)
4382                         continue;
4383                 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
4384                 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
4385                 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
4386                 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
4387                 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
4388                 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
4389                 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
4390                 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
4391                 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
4392                 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
4393                 if (out->firstvertex == -1)
4394                         Mem_Free(out->data_vertex3f);
4395                 if (out->firstelement == -1)
4396                         Mem_Free(out->data_element3i);
4397                 out->firstvertex = totalverts;
4398                 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
4399                 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
4400                 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
4401                 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
4402                 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
4403                 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
4404                 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
4405                 out->firstelement = totaltris * 3;
4406                 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
4407                 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
4408                 //for (j = 0;j < out->numtriangles * 3;j++)
4409                 //      out->data_element3i[j] += totalverts - out->firstvertex;
4410                 totalverts += out->num_vertices;
4411                 totaltris += out->num_triangles;
4412         }
4413         Mem_Free(originaldata_vertex3f);
4414         Mem_Free(originaldata_element3i);
4415         }
4416         */
4417 }
4418
4419 static void Mod_Q3BSP_LoadModels(lump_t *l)
4420 {
4421         q3dmodel_t *in;
4422         q3mmodel_t *out;
4423         int i, j, n, c, count;
4424
4425         in = (void *)(mod_base + l->fileofs);
4426         if (l->filelen % sizeof(*in))
4427                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
4428         count = l->filelen / sizeof(*in);
4429         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4430
4431         loadmodel->brushq3.data_models = out;
4432         loadmodel->brushq3.num_models = count;
4433
4434         for (i = 0;i < count;i++, in++, out++)
4435         {
4436                 for (j = 0;j < 3;j++)
4437                 {
4438                         out->mins[j] = LittleFloat(in->mins[j]);
4439                         out->maxs[j] = LittleFloat(in->maxs[j]);
4440                 }
4441                 n = LittleLong(in->firstface);
4442                 c = LittleLong(in->numfaces);
4443                 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4444                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4445                 out->firstface = loadmodel->brushq3.data_faces + n;
4446                 out->numfaces = c;
4447                 n = LittleLong(in->firstbrush);
4448                 c = LittleLong(in->numbrushes);
4449                 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4450                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4451                 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4452                 out->numbrushes = c;
4453         }
4454 }
4455
4456 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4457 {
4458         int *in;
4459         q3mbrush_t **out;
4460         int i, n, count;
4461
4462         in = (void *)(mod_base + l->fileofs);
4463         if (l->filelen % sizeof(*in))
4464                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4465         count = l->filelen / sizeof(*in);
4466         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4467
4468         loadmodel->brushq3.data_leafbrushes = out;
4469         loadmodel->brushq3.num_leafbrushes = count;
4470
4471         for (i = 0;i < count;i++, in++, out++)
4472         {
4473                 n = LittleLong(*in);
4474                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4475                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4476                 *out = loadmodel->brushq3.data_brushes + n;
4477         }
4478 }
4479
4480 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4481 {
4482         int *in;
4483         q3msurface_t **out;
4484         int i, n, count;
4485
4486         in = (void *)(mod_base + l->fileofs);
4487         if (l->filelen % sizeof(*in))
4488                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4489         count = l->filelen / sizeof(*in);
4490         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4491
4492         loadmodel->brushq3.data_leaffaces = out;
4493         loadmodel->brushq3.num_leaffaces = count;
4494
4495         for (i = 0;i < count;i++, in++, out++)
4496         {
4497                 n = LittleLong(*in);
4498                 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4499                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4500                 *out = loadmodel->brushq3.data_faces + n;
4501         }
4502 }
4503
4504 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4505 {
4506         q3dleaf_t *in;
4507         q3mleaf_t *out;
4508         int i, j, n, c, count;
4509
4510         in = (void *)(mod_base + l->fileofs);
4511         if (l->filelen % sizeof(*in))
4512                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4513         count = l->filelen / sizeof(*in);
4514         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4515
4516         loadmodel->brushq3.data_leafs = out;
4517         loadmodel->brushq3.num_leafs = count;
4518
4519         for (i = 0;i < count;i++, in++, out++)
4520         {
4521                 out->parent = NULL;
4522                 out->plane = NULL;
4523                 out->clusterindex = LittleLong(in->clusterindex);
4524                 out->areaindex = LittleLong(in->areaindex);
4525                 for (j = 0;j < 3;j++)
4526                 {
4527                         // yes the mins/maxs are ints
4528                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4529                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4530                 }
4531                 n = LittleLong(in->firstleafface);
4532                 c = LittleLong(in->numleaffaces);
4533                 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4534                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4535                 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4536                 out->numleaffaces = c;
4537                 n = LittleLong(in->firstleafbrush);
4538                 c = LittleLong(in->numleafbrushes);
4539                 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4540                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4541                 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4542                 out->numleafbrushes = c;
4543         }
4544 }
4545
4546 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4547 {
4548         if (node->parent)
4549                 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4550         node->parent = parent;
4551         if (node->plane)
4552         {
4553                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4554                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4555         }
4556 }
4557
4558 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4559 {
4560         q3dnode_t *in;
4561         q3mnode_t *out;
4562         int i, j, n, count;
4563
4564         in = (void *)(mod_base + l->fileofs);
4565         if (l->filelen % sizeof(*in))
4566                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4567         count = l->filelen / sizeof(*in);
4568         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4569
4570         loadmodel->brushq3.data_nodes = out;
4571         loadmodel->brushq3.num_nodes = count;
4572
4573         for (i = 0;i < count;i++, in++, out++)
4574         {
4575                 out->parent = NULL;
4576                 n = LittleLong(in->planeindex);
4577                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4578                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4579                 out->plane = loadmodel->brushq3.data_planes + n;
4580                 for (j = 0;j < 2;j++)
4581                 {
4582                         n = LittleLong(in->childrenindex[j]);
4583                         if (n >= 0)
4584                         {
4585                                 if (n >= loadmodel->brushq3.num_nodes)
4586                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4587                                 out->children[j] = loadmodel->brushq3.data_nodes + n;
4588                         }
4589                         else
4590                         {
4591                                 n = -1 - n;
4592                                 if (n >= loadmodel->brushq3.num_leafs)
4593                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4594                                 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4595                         }
4596                 }
4597                 for (j = 0;j < 3;j++)
4598                 {
4599                         // yes the mins/maxs are ints
4600                         out->mins[j] = LittleLong(in->mins[j]) - 1;
4601                         out->maxs[j] = LittleLong(in->maxs[j]) + 1;
4602                 }
4603         }
4604
4605         // set the parent pointers
4606         Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4607 }
4608
4609 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4610 {
4611         q3dlightgrid_t *in;
4612         q3dlightgrid_t *out;
4613         int count;
4614
4615         in = (void *)(mod_base + l->fileofs);
4616         if (l->filelen % sizeof(*in))
4617                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4618         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4619         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4620         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4621         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4622         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4623         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4624         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4625         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4626         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4627         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4628         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4629         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4630         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4631         if (l->filelen)
4632         {
4633                 if (l->filelen < count * (int)sizeof(*in))
4634                         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]);
4635                 if (l->filelen != count * (int)sizeof(*in))
4636                         Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4637         }
4638
4639         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4640         loadmodel->brushq3.data_lightgrid = out;
4641         loadmodel->brushq3.num_lightgrid = count;
4642
4643         // no swapping or validation necessary
4644         if (l->filelen)
4645                 memcpy(out, in, count * (int)sizeof(*out));
4646         else
4647         {
4648                 // no data, fill with white
4649                 int i;
4650                 for (i = 0;i < count;i++)
4651                 {
4652                         out[i].ambientrgb[0] = 128;
4653                         out[i].ambientrgb[1] = 128;
4654                         out[i].ambientrgb[2] = 128;
4655                         out[i].diffusergb[0] = 0;
4656                         out[i].diffusergb[1] = 0;
4657                         out[i].diffusergb[2] = 0;
4658                         out[i].diffusepitch = 0;
4659                         out[i].diffuseyaw = 0;
4660                 }
4661         }
4662
4663         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]);
4664         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]);
4665 }
4666
4667 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4668 {
4669         q3dpvs_t *in;
4670         int totalchains;
4671
4672         if (l->filelen == 0)
4673         {
4674                 int i;
4675                 // unvised maps often have cluster indices even without pvs, so check
4676                 // leafs to find real number of clusters
4677                 loadmodel->brush.num_pvsclusters = 1;
4678                 for (i = 0;i < loadmodel->brushq3.num_leafs;i++)
4679                         loadmodel->brush.num_pvsclusters = min(loadmodel->brush.num_pvsclusters, loadmodel->brushq3.data_leafs[i].clusterindex + 1);
4680
4681                 // create clusters
4682                 loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters + 7) / 8;
4683                 totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4684                 loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4685                 memset(loadmodel->brush.data_pvsclusters, 0xFF, totalchains);
4686                 return;
4687         }
4688
4689         in = (void *)(mod_base + l->fileofs);
4690         if (l->filelen < 9)
4691                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4692
4693         loadmodel->brush.num_pvsclusters = LittleLong(in->numclusters);
4694         loadmodel->brush.num_pvsclusterbytes = LittleLong(in->chainlength);
4695         if (loadmodel->brush.num_pvsclusterbytes < ((loadmodel->brush.num_pvsclusters + 7) / 8))
4696                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.num_pvsclusters);
4697         totalchains = loadmodel->brush.num_pvsclusterbytes * loadmodel->brush.num_pvsclusters;
4698         if (l->filelen < totalchains + (int)sizeof(*in))
4699                 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);
4700
4701         loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains);
4702         memcpy(loadmodel->brush.data_pvsclusters, (qbyte *)(in + 1), totalchains);
4703 }
4704
4705 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4706 {
4707         // FIXME: finish this code
4708         VectorCopy(in, out);
4709 }
4710
4711 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4712 {
4713         int i, j, k, index[3];
4714         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4715         q3dlightgrid_t *a, *s;
4716         // FIXME: write this
4717         if (!model->brushq3.num_lightgrid)
4718         {
4719                 ambientcolor[0] = 1;
4720                 ambientcolor[1] = 1;
4721                 ambientcolor[2] = 1;
4722                 return;
4723         }
4724         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4725         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4726         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4727         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4728         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4729         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4730         index[0] = (int)floor(transformed[0]);
4731         index[1] = (int)floor(transformed[1]);
4732         index[2] = (int)floor(transformed[2]);
4733         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4734         // now lerp the values
4735         VectorClear(diffusenormal);
4736         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4737         for (k = 0;k < 2;k++)
4738         {
4739                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4740                 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4741                         continue;
4742                 for (j = 0;j < 2;j++)
4743                 {
4744                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4745                         if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4746                                 continue;
4747                         for (i = 0;i < 2;i++)
4748                         {
4749                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4750                                 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4751                                         continue;
4752                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4753                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4754                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4755                                 pitch = s->diffusepitch * M_PI / 128;
4756                                 yaw = s->diffuseyaw * M_PI / 128;
4757                                 sinpitch = sin(pitch);
4758                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4759                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4760                                 diffusenormal[2] += blend * (cos(pitch));
4761                                 //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)));
4762                         }
4763                 }
4764         }
4765         VectorNormalize(diffusenormal);
4766         //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]);
4767 }
4768
4769 static void Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const vec3_t point, int markframe)
4770 {
4771         int i;
4772         q3mleaf_t *leaf;
4773         colbrushf_t *brush;
4774         // find which leaf the point is in
4775         while (node->plane)
4776                 node = node->children[DotProduct(point, node->plane->normal) < node->plane->dist];
4777         // point trace the brushes
4778         leaf = (q3mleaf_t *)node;
4779         for (i = 0;i < leaf->numleafbrushes;i++)
4780         {
4781                 brush = leaf->firstleafbrush[i]->colbrushf;
4782                 if (brush && brush->markframe != markframe && BoxesOverlap(point, point, brush->mins, brush->maxs))
4783                 {
4784                         brush->markframe = markframe;
4785                         Collision_TracePointBrushFloat(trace, point, leaf->firstleafbrush[i]->colbrushf);
4786                 }
4787         }
4788         // can't do point traces on curves (they have no thickness)
4789 }
4790
4791 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)
4792 {
4793         int i, startside, endside;
4794         float dist1, dist2, midfrac, mid[3], nodesegmentmins[3], nodesegmentmaxs[3];
4795         q3mleaf_t *leaf;
4796         q3msurface_t *face;
4797         colbrushf_t *brush;
4798         if (startfrac > trace->realfraction)
4799                 return;
4800         // note: all line fragments past first impact fraction are ignored
4801         if (VectorCompare(start, end))
4802         {
4803                 // find which leaf the point is in
4804                 while (node->plane)
4805                         node = node->children[DotProduct(start, node->plane->normal) < node->plane->dist];
4806         }
4807         else
4808         {
4809                 // find which nodes the line is in and recurse for them
4810                 while (node->plane)
4811                 {
4812                         // recurse down node sides
4813                         dist1 = PlaneDiff(start, node->plane);
4814                         dist2 = PlaneDiff(end, node->plane);
4815                         startside = dist1 < 0;
4816                         endside = dist2 < 0;
4817                         if (startside == endside)
4818                         {
4819                                 // most of the time the line fragment is on one side of the plane
4820                                 node = node->children[startside];
4821                         }
4822                         else
4823                         {
4824                                 // line crosses node plane, split the line
4825                                 midfrac = dist1 / (dist1 - dist2);
4826                                 VectorLerp(start, midfrac, end, mid);
4827                                 // take the near side first
4828                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4829                                 if (midfrac <= trace->realfraction)
4830                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
4831                                 return;
4832                         }
4833                 }
4834         }
4835         // hit a leaf
4836         nodesegmentmins[0] = min(start[0], end[0]);
4837         nodesegmentmins[1] = min(start[1], end[1]);
4838         nodesegmentmins[2] = min(start[2], end[2]);
4839         nodesegmentmaxs[0] = max(start[0], end[0]);
4840         nodesegmentmaxs[1] = max(start[1], end[1]);
4841         nodesegmentmaxs[2] = max(start[2], end[2]);
4842         // line trace the brushes
4843         leaf = (q3mleaf_t *)node;
4844         for (i = 0;i < leaf->numleafbrushes;i++)
4845         {
4846                 brush = leaf->firstleafbrush[i]->colbrushf;
4847                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4848                 {
4849                         brush->markframe = markframe;
4850                         Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4851                         if (startfrac > trace->realfraction)
4852                                 return;
4853                 }
4854         }
4855         // can't do point traces on curves (they have no thickness)
4856         if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
4857         {
4858                 // line trace the curves
4859                 for (i = 0;i < leaf->numleaffaces;i++)
4860                 {
4861                         face = leaf->firstleafface[i];
4862                         if (face->collisions && face->collisionmarkframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4863                         {
4864                                 face->collisionmarkframe = markframe;
4865                                 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4866                                 if (startfrac > trace->realfraction)
4867                                         return;
4868                         }
4869                 }
4870         }
4871 }
4872
4873 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)
4874 {
4875         int i;
4876         //int sides;
4877         float nodesegmentmins[3], nodesegmentmaxs[3];
4878         q3mleaf_t *leaf;
4879         colbrushf_t *brush;
4880         q3msurface_t *face;
4881         /*
4882                 // find which nodes the line is in and recurse for them
4883                 while (node->plane)
4884                 {
4885                         // recurse down node sides
4886                         int startside, endside;
4887                         float dist1near, dist1far, dist2near, dist2far;
4888                         BoxPlaneCornerDistances(thisbrush_start->mins, thisbrush_start->maxs, node->plane, &dist1near, &dist1far);
4889                         BoxPlaneCornerDistances(thisbrush_end->mins, thisbrush_end->maxs, node->plane, &dist2near, &dist2far);
4890                         startside = dist1near < 0;
4891                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
4892                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
4893                         if (startside == 2 || endside == 2)
4894                         {
4895                                 // brushes cross plane
4896                                 // do not clip anything, just take both sides
4897                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4898                                 node = node->children[1];
4899                                 continue;
4900                         }
4901                         if (startside == 0)
4902                         {
4903                                 if (endside == 0)
4904                                 {
4905                                         node = node->children[0];
4906                                         continue;
4907                                 }
4908                                 else
4909                                 {
4910                                         //midf0 = dist1near / (dist1near - dist2near);
4911                                         //midf1 = dist1far / (dist1far - dist2far);
4912                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4913                                         node = node->children[1];
4914                                         continue;
4915                                 }
4916                         }
4917                         else
4918                         {
4919                                 if (endside == 0)
4920                                 {
4921                                         //midf0 = dist1near / (dist1near - dist2near);
4922                                         //midf1 = dist1far / (dist1far - dist2far);
4923                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4924                                         node = node->children[1];
4925                                         continue;
4926                                 }
4927                                 else
4928                                 {
4929                                         node = node->children[1];
4930                                         continue;
4931                                 }
4932                         }
4933
4934                         if (dist1near <  0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4935                         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;}
4936                         if (dist1near <  0 && dist2near <  0 && dist1far >= 0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4937                         if (dist1near <  0 && dist2near <  0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4938                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4939                         if (dist1near <  0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){}
4940                         if (dist1near <  0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4941                         if (dist1near <  0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4942                         if (dist1near >= 0 && dist2near <  0 && dist1far <  0 && dist2far <  0){node = node->children[1];continue;}
4943                         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;}
4944                         if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far <  0){}
4945                         if (dist1near >= 0 && dist2near <  0 && dist1far >= 0 && dist2far >= 0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4946                         if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far <  0){Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);node = node->children[1];continue;}
4947                         if (dist1near >= 0 && dist2near >= 0 && dist1far <  0 && dist2far >= 0){node = node->children[0];continue;}
4948                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far <  0){node = node->children[0];continue;}
4949                         if (dist1near >= 0 && dist2near >= 0 && dist1far >= 0 && dist2far >= 0){node = node->children[0];continue;}
4950                         {             
4951                                 if (dist2near < 0) // d1n<0 && d2n<0
4952                                 {
4953                                         if (dist2near < 0) // d1n<0 && d2n<0
4954                                         {
4955                                                 if (dist2near < 0) // d1n<0 && d2n<0
4956                                                 {
4957                                                 }
4958                                                 else // d1n<0 && d2n>0
4959                                                 {
4960                                                 }
4961                                         }
4962                                         else // d1n<0 && d2n>0
4963                                         {
4964                                                 if (dist2near < 0) // d1n<0 && d2n<0
4965                                                 {
4966                                                 }
4967                                                 else // d1n<0 && d2n>0
4968                                                 {
4969                                                 }
4970                                         }
4971                                 }
4972                                 else // d1n<0 && d2n>0
4973                                 {
4974                                 }
4975                         }
4976                         else // d1n>0
4977                         {
4978                                 if (dist2near < 0) // d1n>0 && d2n<0
4979                                 {
4980                                 }
4981                                 else // d1n>0 && d2n>0
4982                                 {
4983                                 }
4984                         }
4985                         if (dist1near < 0 == dist1far < 0 == dist2near < 0 == dist2far < 0)
4986                         {
4987                                 node = node->children[startside];
4988                                 continue;
4989                         }
4990                         if (dist1near < dist2near)
4991                         {
4992                                 // out
4993                                 if (dist1near >= 0)
4994                                 {
4995                                         node = node->children[0];
4996                                         continue;
4997                                 }
4998                                 if (dist2far < 0)
4999                                 {
5000                                         node = node->children[1];
5001                                         continue;
5002                                 }
5003                                 // dist1near < 0 && dist2far >= 0
5004                         }
5005                         else
5006                         {
5007                                 // in
5008                         }
5009                         startside = dist1near < 0 ? (dist1far < 0 ? 1 : 2) : (dist1far < 0 ? 2 : 0);
5010                         endside = dist2near < 0 ? (dist2far < 0 ? 1 : 2) : (dist2far < 0 ? 2 : 0);
5011                         if (startside == 2 || endside == 2)
5012                         {
5013                                 // brushes cross plane
5014                                 // do not clip anything, just take both sides
5015                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5016                                 node = node->children[1];
5017                         }
5018                         else if (startside == endside)
5019                                 node = node->children[startside];
5020                         else if (startside == 0) // endside = 1 (start infront, end behind)
5021                         {
5022                         }
5023                         else // startside == 1 endside = 0 (start behind, end infront)
5024                         {
5025                         }
5026                         == endside)
5027                         {
5028                                 if (startside < 2)
5029                                         node = node->children[startside];
5030                                 else
5031                                 {
5032                                         // start and end brush cross plane
5033                                 }
5034                         }
5035                         else
5036                         {
5037                         }
5038                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5039                                 node = node->children[1];
5040                         else if (dist1near < 0 && dist1far < 0 && dist2near >= 0 && dist2far >= 0)
5041                         else if (dist1near >= 0 && dist1far >= 0 && dist2near < 0 && dist2far < 0)
5042                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5043                                 node = node->children[0];
5044                         else
5045                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5046                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5047                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5048                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5049                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5050                         {
5051                         }
5052                         else if (dist1near >= 0 && dist1far >= 0)
5053                         {
5054                         }
5055                         else // mixed (lying on plane)
5056                         {
5057                         }
5058                         {
5059                                 if (dist2near < 0 && dist2far < 0)
5060                                 {
5061                                 }
5062                                 else
5063                                         node = node->children[1];
5064                         }
5065                         if (dist1near < 0 && dist1far < 0 && dist2near < 0 && dist2far < 0)
5066                                 node = node->children[0];
5067                         else if (dist1near >= 0 && dist1far >= 0 && dist2near >= 0 && dist2far >= 0)
5068                                 node = node->children[1];
5069                         else
5070                         {
5071                                 // both sides
5072                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5073                                 node = node->children[1];
5074                         }
5075                         sides = dist1near || dist1near < 0 | dist1far < 0 | dist2near < 0 | dist
5076                         startside = dist1 < 0;
5077                         endside = dist2 < 0;
5078                         if (startside == endside)
5079                         {
5080                                 // most of the time the line fragment is on one side of the plane
5081                                 node = node->children[startside];
5082                         }
5083                         else
5084                         {
5085                                 // line crosses node plane, split the line
5086                                 midfrac = dist1 / (dist1 - dist2);
5087                                 VectorLerp(start, midfrac, end, mid);
5088                                 // take the near side first
5089                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5090                                 if (midfrac <= trace->fraction)
5091                                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs);
5092                                 return;
5093                         }
5094                 }
5095         */
5096 #if 1
5097         for (;;)
5098         {
5099                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5100                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5101                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5102                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5103                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5104                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5105                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5106                         return;
5107                 if (!node->plane)
5108                         break;
5109                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5110                 node = node->children[1];
5111         }
5112 #elif 0
5113         // FIXME: could be made faster by copying TraceLine code and making it use
5114         // box plane distances...  (variant on the BoxOnPlaneSide code)
5115         for (;;)
5116         {
5117                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5118                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5119                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5120                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5121                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5122                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5123                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5124                         return;
5125                 if (!node->plane)
5126                         break;
5127                 if (mod_q3bsp_debugtracebrush.integer == 2)
5128                 {
5129                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5130                         node = node->children[1];
5131                         continue;
5132                 }
5133                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5134                 {
5135                         // recurse down node sides
5136                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5137                         if (sides == 3)
5138                         {
5139                                 // segment box crosses plane
5140                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5141                                 node = node->children[1];
5142                                 continue;
5143                         }
5144                         // take whichever side the segment box is on
5145                         node = node->children[sides - 1];
5146                         continue;
5147                 }
5148                 else
5149                 {
5150                         // recurse down node sides
5151                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5152                         if (sides == 3)
5153                         {
5154                                 // segment box crosses plane
5155                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5156                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5157                                 if (sides == 3)
5158                                 {
5159                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5160                                         node = node->children[1];
5161                                         continue;
5162                                 }
5163                         }
5164                         // take whichever side the segment box is on
5165                         node = node->children[sides - 1];
5166                         continue;
5167                 }
5168                 return;
5169         }
5170 #else
5171         // FIXME: could be made faster by copying TraceLine code and making it use
5172         // box plane distances...  (variant on the BoxOnPlaneSide code)
5173         for (;;)
5174         {
5175                 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
5176                 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
5177                 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
5178                 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
5179                 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
5180                 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
5181                 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
5182                         return;
5183                 if (!node->plane)
5184                         break;
5185                 if (mod_q3bsp_debugtracebrush.integer == 2)
5186                 {
5187                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5188                         node = node->children[1];
5189                 }
5190                 else if (mod_q3bsp_debugtracebrush.integer == 1)
5191                 {
5192                         // recurse down node sides
5193                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5194                         if (sides == 3)
5195                         {
5196                                 // segment box crosses plane
5197                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5198                                 node = node->children[1];
5199                         }
5200                         else
5201                         {
5202                                 // take whichever side the segment box is on
5203                                 node = node->children[sides - 1];
5204                         }
5205                 }
5206                 else
5207                 {
5208                         // recurse down node sides
5209                         sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane);
5210                         if (sides == 3)
5211                         {
5212                                 // segment box crosses plane
5213                                 // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently...
5214                                 sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane);
5215                                 if (sides == 3)
5216                                 {
5217                                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
5218                                         sides = 2;
5219                                 }
5220                         }
5221                         // take whichever side the segment box is on
5222                         node = node->children[sides - 1];
5223                 }
5224         }
5225 #endif
5226         // hit a leaf
5227         leaf = (q3mleaf_t *)node;
5228         for (i = 0;i < leaf->numleafbrushes;i++)
5229         {
5230                 brush = leaf->firstleafbrush[i]->colbrushf;
5231                 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
5232                 {
5233                         brush->markframe = markframe;
5234                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
5235                 }
5236         }
5237         if (mod_q3bsp_curves_collisions.integer)
5238         {
5239                 for (i = 0;i < leaf->numleaffaces;i++)
5240                 {
5241                         face = leaf->firstleafface[i];
5242                         if (face->collisions && face->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
5243                         {
5244                                 face->markframe = markframe;
5245                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5246                         }
5247                 }
5248         }
5249 }
5250
5251 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)
5252 {
5253         int i;
5254         float segmentmins[3], segmentmaxs[3];
5255         colbrushf_t *thisbrush_start, *thisbrush_end;
5256         matrix4x4_t startmatrix, endmatrix;
5257         static int markframe = 0;
5258         q3msurface_t *face;
5259         memset(trace, 0, sizeof(*trace));
5260         trace->fraction = 1;
5261         trace->realfraction = 1;
5262         trace->hitsupercontentsmask = hitsupercontentsmask;
5263         Matrix4x4_CreateIdentity(&startmatrix);
5264         Matrix4x4_CreateIdentity(&endmatrix);
5265         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
5266         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
5267         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
5268         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
5269         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
5270         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
5271         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
5272         {
5273                 if (VectorCompare(boxstartmins, boxendmins))
5274                 {
5275                         // point trace
5276                         if (model->brushq3.submodel)
5277                         {
5278                                 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5279                                         if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5280                                                 Collision_TracePointBrushFloat(trace, boxstartmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5281                         }
5282                         else
5283                                 Mod_Q3BSP_TracePoint_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, ++markframe);
5284                 }
5285                 else
5286                 {
5287                         // line trace
5288                         if (model->brushq3.submodel)
5289                         {
5290                                 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5291                                         if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5292                                                 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5293                                 if (mod_q3bsp_curves_collisions.integer)
5294                                 {
5295                                         for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
5296                                         {
5297                                                 face = model->brushq3.data_thismodel->firstface + i;
5298                                                 if (face->collisions)
5299                                                         Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5300                                         }
5301                                 }
5302                         }
5303                         else
5304                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe, segmentmins, segmentmaxs);
5305                 }
5306         }
5307         else
5308         {
5309                 // box trace, performed as brush trace
5310                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
5311                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
5312                 if (model->brushq3.submodel)
5313                 {
5314                         for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
5315                                 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
5316                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
5317                         if (mod_q3bsp_curves_collisions.integer)
5318                         {
5319                                 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
5320                                 {
5321                                         face = model->brushq3.data_thismodel->firstface + i;
5322                                         if (face->collisions)
5323                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_collisiontriangles, face->data_collisionelement3i, face->data_collisionvertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
5324                                 }
5325                         }
5326                 }
5327                 else
5328                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
5329         }
5330 }
5331
5332 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
5333 {
5334         int clusterindex, side, nodestackindex = 0;
5335         q3mnode_t *node, *nodestack[1024];
5336         node = model->brushq3.data_nodes;
5337         if (!model->brush.num_pvsclusters)
5338                 return true;
5339         for (;;)
5340         {
5341                 if (node->plane)
5342                 {
5343                         // node - recurse down the BSP tree
5344                         side = BoxOnPlaneSide(mins, maxs, node->plane) - 1;
5345                         if (side < 2)
5346                         {
5347                                 // box is on one side of plane, take that path
5348                                 node = node->children[side];
5349                         }
5350                         else
5351                         {
5352                                 // box crosses plane, take one path and remember the other
5353                                 if (nodestackindex < 1024)
5354                                         nodestack[nodestackindex++] = node->children[0];
5355                                 node = node->children[1];
5356                         }
5357                 }
5358                 else
5359                 {
5360                         // leaf - check cluster bit
5361                         clusterindex = ((q3mleaf_t *)node)->clusterindex;
5362 #if 0
5363                         if (clusterindex >= model->brush.num_pvsclusters)
5364                         {
5365                                 Con_Printf("%i >= %i\n", clusterindex, model->brush.num_pvsclusters);
5366                                 return true;
5367                         }
5368 #endif
5369                         if (CHECKPVSBIT(pvs, clusterindex))
5370                         {
5371                                 // it is visible, return immediately with the news
5372                                 return true;
5373                         }
5374                         else
5375                         {
5376                                 // nothing to see here, try another path we didn't take earlier
5377                                 if (nodestackindex == 0)
5378                                         break;
5379                                 node = nodestack[--nodestackindex];
5380                         }
5381                 }
5382         }
5383         // it is not visible
5384         return false;
5385 }
5386
5387 //Returns PVS data for a given point
5388 //(note: can return NULL)
5389 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
5390 {
5391         q3mnode_t *node;
5392         Mod_CheckLoaded(model);
5393         node = model->brushq3.data_nodes;
5394         while (node->plane)
5395                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
5396         if (((q3mleaf_t *)node)->clusterindex >= 0)
5397                 return model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5398         else
5399                 return NULL;
5400 }
5401
5402 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
5403 {
5404         while (node->plane)
5405         {
5406                 float d = PlaneDiff(org, node->plane);
5407                 if (d > radius)
5408                         node = node->children[0];
5409                 else if (d < -radius)
5410                         node = node->children[1];
5411                 else
5412                 {
5413                         // go down both sides
5414                         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
5415                         node = node->children[1];
5416                 }
5417         }
5418         // if this leaf is in a cluster, accumulate the pvs bits
5419         if (((q3mleaf_t *)node)->clusterindex >= 0)
5420         {
5421                 int i;
5422                 qbyte *pvs = model->brush.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
5423                 for (i = 0;i < pvsbytes;i++)
5424                         pvsbuffer[i] |= pvs[i];
5425         }
5426 }
5427
5428 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
5429 //of the given point.
5430 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
5431 {
5432         int bytes = model->brush.num_pvsclusterbytes;
5433         bytes = min(bytes, pvsbufferlength);
5434         if (r_novis.integer || !model->brush.num_pvsclusters || !Mod_Q3BSP_GetPVS(model, org))
5435         {
5436                 memset(pvsbuffer, 0xFF, bytes);
5437                 return bytes;
5438         }
5439         memset(pvsbuffer, 0, bytes);
5440         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
5441         return bytes;
5442 }
5443
5444
5445 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
5446 {
5447         int supercontents = 0;
5448         if (nativecontents & Q2CONTENTS_SOLID)
5449                 supercontents |= SUPERCONTENTS_SOLID;
5450         if (nativecontents & Q2CONTENTS_WATER)
5451                 supercontents |= SUPERCONTENTS_WATER;
5452         if (nativecontents & Q2CONTENTS_SLIME)
5453                 supercontents |= SUPERCONTENTS_SLIME;
5454         if (nativecontents & Q2CONTENTS_LAVA)
5455                 supercontents |= SUPERCONTENTS_LAVA;
5456         return supercontents;
5457 }
5458
5459 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
5460 {
5461         int nativecontents = 0;
5462         if (supercontents & SUPERCONTENTS_SOLID)
5463                 nativecontents |= Q2CONTENTS_SOLID;
5464         if (supercontents & SUPERCONTENTS_WATER)
5465                 nativecontents |= Q2CONTENTS_WATER;
5466         if (supercontents & SUPERCONTENTS_SLIME)
5467                 nativecontents |= Q2CONTENTS_SLIME;
5468         if (supercontents & SUPERCONTENTS_LAVA)
5469                 nativecontents |= Q2CONTENTS_LAVA;
5470         return nativecontents;
5471 }
5472
5473 /*
5474 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)
5475 {
5476         mleaf_t *leaf;
5477         for (;;)
5478         {
5479                 if (!BoxesOverlap(node->mins, node->maxs, mins, maxs))
5480                         return;
5481                 if (!node->plane)
5482                         break;
5483                 Mod_Q3BSP_RecursiveGetVisible(node->children[0], model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
5484                 node = node->children[1];
5485         }
5486         leaf = (mleaf_t *)node;
5487         if ((pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
5488         {
5489                 int marksurfacenum;
5490                 q3msurface_t *surf;
5491                 if (maxleafs && *numleafs < maxleafs)
5492                         leaflist[(*numleaf)++] = leaf;
5493                 if (maxsurfaces)
5494                 {
5495                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
5496                         {
5497                                 face = leaf->firstleafface[marksurfacenum];
5498                                 if (face->shadowmark != shadowmarkcount)
5499                                 {
5500                                         face->shadowmark = shadowmarkcount;
5501                                         if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces)
5502                                                 surfacelist[(*numsurfaces)++] = face;
5503                                 }
5504                         }
5505                 }
5506         }
5507 }
5508
5509 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)
5510 {
5511         // FIXME: support portals
5512         if (maxsurfaces)
5513                 *numsurfaces = 0;
5514         if (maxleafs)
5515                 *numleafs = 0;
5516         if (model->submodel)
5517         {
5518                 if (maxsurfaces)
5519                 {
5520                         for (marksurfacenum = 0;marksurfacenum < leaf->nummarksurfaces;marksurfacenum++)
5521                         {
5522                                 face = ent->model->brushq3.surfaces + leaf->firstmarksurface[marksurfacenum];
5523                                 if (BoxesOverlap(mins, maxs, face->mins, face->maxs) && *numsurfaces < maxsurfaces)
5524                                         surfacelist[(*numsurfaces)++] = face;
5525                         }
5526                 }
5527         }
5528         else
5529         {
5530                 pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
5531                 Mod_Q3BSP_RecursiveGetVisible(ent->model->brushq3.data_nodes, model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
5532         }
5533 }
5534 */
5535
5536 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
5537 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
5538 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);
5539 extern void R_Q3BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
5540 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);
5541 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
5542 {
5543         int i, j, numshadowmeshtriangles;
5544         q3dheader_t *header;
5545         float corner[3], yawradius, modelradius;
5546         q3msurface_t *face;
5547
5548         mod->type = mod_brushq3;
5549         mod->numframes = 1;
5550         mod->numskins = 1;
5551
5552         header = (q3dheader_t *)buffer;
5553
5554         i = LittleLong(header->version);
5555         if (i != Q3BSPVERSION)
5556                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
5557         if (mod->isworldmodel)
5558         {
5559                 Cvar_SetValue("halflifebsp", false);
5560                 // until we get a texture for it...
5561                 R_ResetQuakeSky();
5562         }
5563
5564         mod->soundfromcenter = true;
5565         mod->TraceBox = Mod_Q3BSP_TraceBox;
5566         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
5567         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
5568         mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
5569         mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
5570         mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
5571         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
5572         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5573         //mod->DrawSky = R_Q3BSP_DrawSky;
5574         mod->Draw = R_Q3BSP_Draw;
5575         mod->GetLightInfo = R_Q3BSP_GetLightInfo;
5576         mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
5577         mod->DrawLight = R_Q3BSP_DrawLight;
5578
5579         mod_base = (qbyte *)header;
5580
5581         // swap all the lumps
5582         for (i = 0;i < (int) sizeof(*header) / 4;i++)
5583                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
5584
5585         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
5586         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
5587         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
5588         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
5589         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
5590         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
5591         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
5592         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
5593         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
5594         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
5595         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
5596         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
5597         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
5598         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
5599         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
5600         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
5601         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
5602         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
5603
5604         // make a single combined shadow mesh to allow optimized shadow volume creation
5605         numshadowmeshtriangles = 0;
5606         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5607         {
5608                 face->num_firstshadowmeshtriangle = numshadowmeshtriangles;
5609                 numshadowmeshtriangles += face->num_triangles;
5610         }
5611         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
5612         for (j = 0, face = loadmodel->brushq3.data_faces;j < loadmodel->brushq3.num_faces;j++, face++)
5613                 Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
5614         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
5615         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
5616         
5617         for (i = 0;i < loadmodel->brushq3.num_models;i++)
5618         {
5619                 if (i == 0)
5620                         mod = loadmodel;
5621                 else
5622                 {
5623                         char name[10];
5624                         // LordHavoc: only register submodels if it is the world
5625                         // (prevents bsp models from replacing world submodels)
5626                         if (!loadmodel->isworldmodel)
5627                                 continue;
5628                         // duplicate the basic information
5629                         sprintf(name, "*%i", i);
5630                         mod = Mod_FindName(name);
5631                         *mod = *loadmodel;
5632                         strcpy(mod->name, name);
5633                         // textures and memory belong to the main model
5634                         mod->texturepool = NULL;
5635                         mod->mempool = NULL;
5636                         mod->brush.GetPVS = NULL;
5637                         mod->brush.FatPVS = NULL;
5638                         mod->brush.BoxTouchingPVS = NULL;
5639                         mod->brush.LightPoint = NULL;
5640                         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
5641                 }
5642                 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
5643                 mod->brushq3.submodel = i;
5644
5645                 // make the model surface list (used by shadowing/lighting)
5646                 mod->nummodelsurfaces = mod->brushq3.data_thismodel->numfaces;
5647                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
5648                 for (j = 0;j < mod->nummodelsurfaces;j++)
5649                         mod->surfacelist[j] = (mod->brushq3.data_thismodel->firstface - mod->brushq3.data_faces) + j;
5650
5651                 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
5652                 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
5653                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
5654                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
5655                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
5656                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
5657                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
5658                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
5659                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
5660                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
5661                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
5662                 mod->yawmins[2] = mod->normalmins[2];
5663                 mod->yawmaxs[2] = mod->normalmaxs[2];
5664                 mod->radius = modelradius;
5665                 mod->radius2 = modelradius * modelradius;
5666
5667                 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
5668                         if (mod->brushq3.data_thismodel->firstface[j].texture->surfaceflags & Q3SURFACEFLAG_SKY)
5669                                 break;
5670                 if (j < mod->brushq3.data_thismodel->numfaces)
5671                         mod->DrawSky = R_Q3BSP_DrawSky;
5672         }
5673 }
5674
5675 void Mod_IBSP_Load(model_t *mod, void *buffer)
5676 {
5677         int i = LittleLong(((int *)buffer)[1]);
5678         if (i == Q3BSPVERSION)
5679                 Mod_Q3BSP_Load(mod,buffer);
5680         else if (i == Q2BSPVERSION)
5681                 Mod_Q2BSP_Load(mod,buffer);
5682         else
5683                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
5684 }
5685
5686 void Mod_MAP_Load(model_t *mod, void *buffer)
5687 {
5688         Host_Error("Mod_MAP_Load: not yet implemented\n");
5689 }
5690