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