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