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