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