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