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