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