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