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