]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - model_brush.c
now supports skyboxes in q3bsp (note: must use the "sky" key in worldspawn to set...
[xonotic/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24 #include "winding.h"
25 #include "curves.h"
26
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
28
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
30
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
38 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
39 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
40
41 void Mod_BrushInit(void)
42 {
43 //      Cvar_RegisterVariable(&r_subdivide_size);
44         Cvar_RegisterVariable(&halflifebsp);
45         Cvar_RegisterVariable(&r_novis);
46         Cvar_RegisterVariable(&r_miplightmaps);
47         Cvar_RegisterVariable(&r_lightmaprgba);
48         Cvar_RegisterVariable(&r_nosurftextures);
49         Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
50         Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
51         Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
52         memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
53 }
54
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
56 {
57         mnode_t *node;
58
59         if (model == NULL)
60                 return NULL;
61
62         Mod_CheckLoaded(model);
63
64         // LordHavoc: modified to start at first clip node,
65         // in other words: first node of the (sub)model
66         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
67         while (node->contents == 0)
68                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
69
70         return (mleaf_t *)node;
71 }
72
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
74 {
75         int i;
76         mleaf_t *leaf;
77         leaf = Mod_Q1BSP_PointInLeaf(model, p);
78         if (leaf)
79         {
80                 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
81                 if (i)
82                 {
83                         memcpy(out, leaf->ambient_sound_level, i);
84                         out += i;
85                         outsize -= i;
86                 }
87         }
88         if (outsize)
89                 memset(out, 0, outsize);
90 }
91
92
93 static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
94 {
95         int leafnum;
96 loc0:
97         if (node->contents < 0)
98         {
99                 // leaf
100                 if (node->contents == CONTENTS_SOLID)
101                         return false;
102                 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103                 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
104         }
105
106         // node - recurse down the BSP tree
107         switch (BoxOnPlaneSide(mins, maxs, node->plane))
108         {
109         case 1: // front
110                 node = node->children[0];
111                 goto loc0;
112         case 2: // back
113                 node = node->children[1];
114                 goto loc0;
115         default: // crossing
116                 if (node->children[0]->contents != CONTENTS_SOLID)
117                         if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
118                                 return true;
119                 node = node->children[1];
120                 goto loc0;
121         }
122         // never reached
123         return false;
124 }
125
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
127 {
128         return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
129 }
130
131 /*
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
133 {
134         mnode_t *node;
135
136         if (model == NULL)
137                 return CONTENTS_EMPTY;
138
139         Mod_CheckLoaded(model);
140
141         // LordHavoc: modified to start at first clip node,
142         // in other words: first node of the (sub)model
143         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
144         while (node->contents == 0)
145                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
146
147         return ((mleaf_t *)node)->contents;
148 }
149 */
150
151 typedef struct findnonsolidlocationinfo_s
152 {
153         vec3_t center;
154         vec_t radius;
155         vec3_t nudge;
156         vec_t bestdist;
157         model_t *model;
158 }
159 findnonsolidlocationinfo_t;
160
161 #if 0
162 extern cvar_t samelevel;
163 #endif
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
165 {
166         int i, surfnum, k, *tri, *mark;
167         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
168 #if 0
169         float surfnormal[3];
170 #endif
171         msurface_t *surf;
172         surfmesh_t *mesh;
173         for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
174         {
175                 surf = info->model->brushq1.surfaces + *mark;
176                 if (surf->flags & SURF_SOLIDCLIP)
177                 {
178 #if 0
179                         VectorCopy(surf->plane->normal, surfnormal);
180                         if (surf->flags & SURF_PLANEBACK)
181                                 VectorNegate(surfnormal, surfnormal);
182 #endif
183                         for (mesh = surf->mesh;mesh;mesh = mesh->chain)
184                         {
185                                 for (k = 0;k < mesh->numtriangles;k++)
186                                 {
187                                         tri = mesh->element3i + k * 3;
188                                         VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
189                                         VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
190                                         VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
191                                         VectorSubtract(vert[1], vert[0], edge[0]);
192                                         VectorSubtract(vert[2], vert[1], edge[1]);
193                                         CrossProduct(edge[1], edge[0], facenormal);
194                                         if (facenormal[0] || facenormal[1] || facenormal[2])
195                                         {
196                                                 VectorNormalize(facenormal);
197 #if 0
198                                                 if (VectorDistance(facenormal, surfnormal) > 0.01f)
199                                                         Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
200 #endif
201                                                 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
202                                                 if (f <= info->bestdist && f >= -info->bestdist)
203                                                 {
204                                                         VectorSubtract(vert[0], vert[2], edge[2]);
205                                                         VectorNormalize(edge[0]);
206                                                         VectorNormalize(edge[1]);
207                                                         VectorNormalize(edge[2]);
208                                                         CrossProduct(facenormal, edge[0], edgenormal[0]);
209                                                         CrossProduct(facenormal, edge[1], edgenormal[1]);
210                                                         CrossProduct(facenormal, edge[2], edgenormal[2]);
211 #if 0
212                                                         if (samelevel.integer & 1)
213                                                                 VectorNegate(edgenormal[0], edgenormal[0]);
214                                                         if (samelevel.integer & 2)
215                                                                 VectorNegate(edgenormal[1], edgenormal[1]);
216                                                         if (samelevel.integer & 4)
217                                                                 VectorNegate(edgenormal[2], edgenormal[2]);
218                                                         for (i = 0;i < 3;i++)
219                                                                 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
220                                                                  || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
221                                                                  || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
222                                                                         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]);
223 #endif
224                                                         // face distance
225                                                         if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
226                                                          && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
227                                                          && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
228                                                         {
229                                                                 // we got lucky, the center is within the face
230                                                                 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
231                                                                 if (dist < 0)
232                                                                 {
233                                                                         dist = -dist;
234                                                                         if (info->bestdist > dist)
235                                                                         {
236                                                                                 info->bestdist = dist;
237                                                                                 VectorScale(facenormal, (info->radius - -dist), info->nudge);
238                                                                         }
239                                                                 }
240                                                                 else
241                                                                 {
242                                                                         if (info->bestdist > dist)
243                                                                         {
244                                                                                 info->bestdist = dist;
245                                                                                 VectorScale(facenormal, (info->radius - dist), info->nudge);
246                                                                         }
247                                                                 }
248                                                         }
249                                                         else
250                                                         {
251                                                                 // check which edge or vertex the center is nearest
252                                                                 for (i = 0;i < 3;i++)
253                                                                 {
254                                                                         f = DotProduct(info->center, edge[i]);
255                                                                         if (f >= DotProduct(vert[0], edge[i])
256                                                                          && f <= DotProduct(vert[1], edge[i]))
257                                                                         {
258                                                                                 // on edge
259                                                                                 VectorMA(info->center, -f, edge[i], point);
260                                                                                 dist = sqrt(DotProduct(point, point));
261                                                                                 if (info->bestdist > dist)
262                                                                                 {
263                                                                                         info->bestdist = dist;
264                                                                                         VectorScale(point, (info->radius / dist), info->nudge);
265                                                                                 }
266                                                                                 // skip both vertex checks
267                                                                                 // (both are further away than this edge)
268                                                                                 i++;
269                                                                         }
270                                                                         else
271                                                                         {
272                                                                                 // not on edge, check first vertex of edge
273                                                                                 VectorSubtract(info->center, vert[i], point);
274                                                                                 dist = sqrt(DotProduct(point, point));
275                                                                                 if (info->bestdist > dist)
276                                                                                 {
277                                                                                         info->bestdist = dist;
278                                                                                         VectorScale(point, (info->radius / dist), info->nudge);
279                                                                                 }
280                                                                         }
281                                                                 }
282                                                         }
283                                                 }
284                                         }
285                                 }
286                         }
287                 }
288         }
289 }
290
291 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
292 {
293         if (node->contents)
294         {
295                 if (((mleaf_t *)node)->nummarksurfaces)
296                         Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
297         }
298         else
299         {
300                 float f = PlaneDiff(info->center, node->plane);
301                 if (f >= -info->bestdist)
302                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
303                 if (f <= info->bestdist)
304                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
305         }
306 }
307
308 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
309 {
310         int i;
311         findnonsolidlocationinfo_t info;
312         if (model == NULL)
313         {
314                 VectorCopy(in, out);
315                 return;
316         }
317         VectorCopy(in, info.center);
318         info.radius = radius;
319         info.model = model;
320         i = 0;
321         do
322         {
323                 VectorClear(info.nudge);
324                 info.bestdist = radius;
325                 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
326                 VectorAdd(info.center, info.nudge, info.center);
327         }
328         while (info.bestdist < radius && ++i < 10);
329         VectorCopy(info.center, out);
330 }
331
332 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
333 {
334         switch(nativecontents)
335         {
336                 case CONTENTS_EMPTY:
337                         return 0;
338                 case CONTENTS_SOLID:
339                         return SUPERCONTENTS_SOLID;
340                 case CONTENTS_WATER:
341                         return SUPERCONTENTS_WATER;
342                 case CONTENTS_SLIME:
343                         return SUPERCONTENTS_SLIME;
344                 case CONTENTS_LAVA:
345                         return SUPERCONTENTS_LAVA;
346                 case CONTENTS_SKY:
347                         return SUPERCONTENTS_SKY;
348         }
349         return 0;
350 }
351
352 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
353 {
354         if (supercontents & SUPERCONTENTS_SOLID)
355                 return CONTENTS_SOLID;
356         if (supercontents & SUPERCONTENTS_SKY)
357                 return CONTENTS_SKY;
358         if (supercontents & SUPERCONTENTS_LAVA)
359                 return CONTENTS_LAVA;
360         if (supercontents & SUPERCONTENTS_SLIME)
361                 return CONTENTS_SLIME;
362         if (supercontents & SUPERCONTENTS_WATER)
363                 return CONTENTS_WATER;
364         return CONTENTS_EMPTY;
365 }
366
367 typedef struct
368 {
369         // the hull we're tracing through
370         const hull_t *hull;
371
372         // the trace structure to fill in
373         trace_t *trace;
374
375         // start, end, and end - start (in model space)
376         double start[3];
377         double end[3];
378         double dist[3];
379 }
380 RecursiveHullCheckTraceInfo_t;
381
382 // 1/32 epsilon to keep floating point happy
383 #define DIST_EPSILON (0.03125)
384
385 #define HULLCHECKSTATE_EMPTY 0
386 #define HULLCHECKSTATE_SOLID 1
387 #define HULLCHECKSTATE_DONE 2
388
389 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
390 {
391         // status variables, these don't need to be saved on the stack when
392         // recursing...  but are because this should be thread-safe
393         // (note: tracing against a bbox is not thread-safe, yet)
394         int ret;
395         mplane_t *plane;
396         double t1, t2;
397
398         // variables that need to be stored on the stack when recursing
399         dclipnode_t *node;
400         int side;
401         double midf, mid[3];
402
403         // LordHavoc: a goto!  everyone flee in terror... :)
404 loc0:
405         // check for empty
406         if (num < 0)
407         {
408                 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
409                 if (!t->trace->startfound)
410                 {
411                         t->trace->startfound = true;
412                         t->trace->startsupercontents |= num;
413                 }
414                 if (num & SUPERCONTENTS_LIQUIDSMASK)
415                         t->trace->inwater = true;
416                 if (num == 0)
417                         t->trace->inopen = true;
418                 if (num & t->trace->hitsupercontentsmask)
419                 {
420                         // if the first leaf is solid, set startsolid
421                         if (t->trace->allsolid)
422                                 t->trace->startsolid = true;
423                         return HULLCHECKSTATE_SOLID;
424                 }
425                 else
426                 {
427                         t->trace->allsolid = false;
428                         return HULLCHECKSTATE_EMPTY;
429                 }
430         }
431
432         // find the point distances
433         node = t->hull->clipnodes + num;
434
435         plane = t->hull->planes + node->planenum;
436         if (plane->type < 3)
437         {
438                 t1 = p1[plane->type] - plane->dist;
439                 t2 = p2[plane->type] - plane->dist;
440         }
441         else
442         {
443                 t1 = DotProduct (plane->normal, p1) - plane->dist;
444                 t2 = DotProduct (plane->normal, p2) - plane->dist;
445         }
446
447         if (t1 < 0)
448         {
449                 if (t2 < 0)
450                 {
451                         num = node->children[1];
452                         goto loc0;
453                 }
454                 side = 1;
455         }
456         else
457         {
458                 if (t2 >= 0)
459                 {
460                         num = node->children[0];
461                         goto loc0;
462                 }
463                 side = 0;
464         }
465
466         // the line intersects, find intersection point
467         // LordHavoc: this uses the original trace for maximum accuracy
468         if (plane->type < 3)
469         {
470                 t1 = t->start[plane->type] - plane->dist;
471                 t2 = t->end[plane->type] - plane->dist;
472         }
473         else
474         {
475                 t1 = DotProduct (plane->normal, t->start) - plane->dist;
476                 t2 = DotProduct (plane->normal, t->end) - plane->dist;
477         }
478
479         midf = t1 / (t1 - t2);
480         midf = bound(p1f, midf, p2f);
481         VectorMA(t->start, midf, t->dist, mid);
482
483         // recurse both sides, front side first
484         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
485         // if this side is not empty, return what it is (solid or done)
486         if (ret != HULLCHECKSTATE_EMPTY)
487                 return ret;
488
489         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
490         // if other side is not solid, return what it is (empty or done)
491         if (ret != HULLCHECKSTATE_SOLID)
492                 return ret;
493
494         // front is air and back is solid, this is the impact point...
495         if (side)
496         {
497                 t->trace->plane.dist = -plane->dist;
498                 VectorNegate (plane->normal, t->trace->plane.normal);
499         }
500         else
501         {
502                 t->trace->plane.dist = plane->dist;
503                 VectorCopy (plane->normal, t->trace->plane.normal);
504         }
505
506         // bias away from surface a bit
507         t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
508         t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
509
510         midf = t1 / (t1 - t2);
511         t->trace->fraction = bound(0.0f, midf, 1.0);
512
513         return HULLCHECKSTATE_DONE;
514 }
515
516 static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
517 {
518         // this function currently only supports same size start and end
519         double boxsize[3];
520         RecursiveHullCheckTraceInfo_t rhc;
521
522         memset(&rhc, 0, sizeof(rhc));
523         memset(trace, 0, sizeof(trace_t));
524         rhc.trace = trace;
525         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
526         rhc.trace->fraction = 1;
527         rhc.trace->allsolid = true;
528         VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
529         if (boxsize[0] < 3)
530                 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
531         else if (model->brush.ishlbsp)
532         {
533                 if (boxsize[0] <= 32)
534                 {
535                         if (boxsize[2] < 54) // pick the nearest of 36 or 72
536                                 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
537                         else
538                                 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
539                 }
540                 else
541                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
542         }
543         else
544         {
545                 if (boxsize[0] <= 32)
546                         rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
547                 else
548                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
549         }
550         VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
551         VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
552         VectorSubtract(rhc.end, rhc.start, rhc.dist);
553         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
554 }
555
556 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)
557 {
558         int side, distz = endz - startz;
559         float front, back;
560         float mid;
561
562 loc0:
563         if (node->contents < 0)
564                 return false;           // didn't hit anything
565
566         switch (node->plane->type)
567         {
568         case PLANE_X:
569                 node = node->children[x < node->plane->dist];
570                 goto loc0;
571         case PLANE_Y:
572                 node = node->children[y < node->plane->dist];
573                 goto loc0;
574         case PLANE_Z:
575                 side = startz < node->plane->dist;
576                 if ((endz < node->plane->dist) == side)
577                 {
578                         node = node->children[side];
579                         goto loc0;
580                 }
581                 // found an intersection
582                 mid = node->plane->dist;
583                 break;
584         default:
585                 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
586                 front += startz * node->plane->normal[2];
587                 back += endz * node->plane->normal[2];
588                 side = front < node->plane->dist;
589                 if ((back < node->plane->dist) == side)
590                 {
591                         node = node->children[side];
592                         goto loc0;
593                 }
594                 // found an intersection
595                 mid = startz + distz * (front - node->plane->dist) / (front - back);
596                 break;
597         }
598
599         // go down front side
600         if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
601                 return true;    // hit something
602         else
603         {
604                 // check for impact on this node
605                 if (node->numsurfaces)
606                 {
607                         int i, ds, dt;
608                         msurface_t *surf;
609
610                         surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
611                         for (i = 0;i < node->numsurfaces;i++, surf++)
612                         {
613                                 if (!(surf->flags & SURF_LIGHTMAP))
614                                         continue;       // no lightmaps
615
616                                 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]);
617                                 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]);
618
619                                 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
620                                         continue;
621
622                                 ds -= surf->texturemins[0];
623                                 dt -= surf->texturemins[1];
624
625                                 if (ds > surf->extents[0] || dt > surf->extents[1])
626                                         continue;
627
628                                 if (surf->samples)
629                                 {
630                                         qbyte *lightmap;
631                                         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;
632                                         line3 = ((surf->extents[0]>>4)+1)*3;
633                                         size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
634
635                                         lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
636
637                                         for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
638                                         {
639                                                 scale = d_lightstylevalue[surf->styles[maps]];
640                                                 r00 += lightmap[      0] * scale;g00 += lightmap[      1] * scale;b00 += lightmap[      2] * scale;
641                                                 r01 += lightmap[      3] * scale;g01 += lightmap[      4] * scale;b01 += lightmap[      5] * scale;
642                                                 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
643                                                 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
644                                                 lightmap += size3;
645                                         }
646
647 /*
648 LordHavoc: here's the readable version of the interpolation
649 code, not quite as easy for the compiler to optimize...
650
651 dsfrac is the X position in the lightmap pixel, * 16
652 dtfrac is the Y position in the lightmap pixel, * 16
653 r00 is top left corner, r01 is top right corner
654 r10 is bottom left corner, r11 is bottom right corner
655 g and b are the same layout.
656 r0 and r1 are the top and bottom intermediate results
657
658 first we interpolate the top two points, to get the top
659 edge sample
660
661         r0 = (((r01-r00) * dsfrac) >> 4) + r00;
662         g0 = (((g01-g00) * dsfrac) >> 4) + g00;
663         b0 = (((b01-b00) * dsfrac) >> 4) + b00;
664
665 then we interpolate the bottom two points, to get the
666 bottom edge sample
667
668         r1 = (((r11-r10) * dsfrac) >> 4) + r10;
669         g1 = (((g11-g10) * dsfrac) >> 4) + g10;
670         b1 = (((b11-b10) * dsfrac) >> 4) + b10;
671
672 then we interpolate the top and bottom samples to get the
673 middle sample (the one which was requested)
674
675         r = (((r1-r0) * dtfrac) >> 4) + r0;
676         g = (((g1-g0) * dtfrac) >> 4) + g0;
677         b = (((b1-b0) * dtfrac) >> 4) + b0;
678 */
679
680                                         ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
681                                         ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
682                                         ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
683                                 }
684                                 return true; // success
685                         }
686                 }
687
688                 // go down back side
689                 node = node->children[side ^ 1];
690                 startz = mid;
691                 distz = endz - startz;
692                 goto loc0;
693         }
694 }
695
696 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
697 {
698         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);
699 }
700
701 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
702 {
703         int c;
704         while (out < outend)
705         {
706                 if (in == inend)
707                 {
708                         Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
709                         return;
710                 }
711                 c = *in++;
712                 if (c)
713                         *out++ = c;
714                 else
715                 {
716                         for (c = *in++;c > 0;c--)
717                         {
718                                 if (out == outend)
719                                 {
720                                         Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
721                                         return;
722                                 }
723                                 *out++ = 0;
724                         }
725                 }
726         }
727 }
728
729 static void Mod_Q1BSP_LoadTextures(lump_t *l)
730 {
731         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
732         miptex_t *dmiptex;
733         texture_t *tx, *tx2, *anims[10], *altanims[10];
734         dmiptexlump_t *m;
735         qbyte *data, *mtdata;
736         char name[256];
737
738         loadmodel->brushq1.textures = NULL;
739
740         // add two slots for notexture walls and notexture liquids
741         if (l->filelen)
742         {
743                 m = (dmiptexlump_t *)(mod_base + l->fileofs);
744                 m->nummiptex = LittleLong (m->nummiptex);
745                 loadmodel->brushq1.numtextures = m->nummiptex + 2;
746         }
747         else
748         {
749                 m = NULL;
750                 loadmodel->brushq1.numtextures = 2;
751         }
752
753         loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
754
755         // fill out all slots with notexture
756         for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
757         {
758                 tx->number = i;
759                 strcpy(tx->name, "NO TEXTURE FOUND");
760                 tx->width = 16;
761                 tx->height = 16;
762                 tx->skin.base = r_notexture;
763                 tx->shader = &Cshader_wall_lightmap;
764                 tx->flags = SURF_SOLIDCLIP;
765                 if (i == loadmodel->brushq1.numtextures - 1)
766                 {
767                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
768                         tx->shader = &Cshader_water;
769                 }
770                 tx->currentframe = tx;
771         }
772
773         if (!m)
774                 return;
775
776         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
777         dofs = m->dataofs;
778         // LordHavoc: mostly rewritten map texture loader
779         for (i = 0;i < m->nummiptex;i++)
780         {
781                 dofs[i] = LittleLong(dofs[i]);
782                 if (dofs[i] == -1 || r_nosurftextures.integer)
783                         continue;
784                 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
785
786                 // make sure name is no more than 15 characters
787                 for (j = 0;dmiptex->name[j] && j < 15;j++)
788                         name[j] = dmiptex->name[j];
789                 name[j] = 0;
790
791                 mtwidth = LittleLong(dmiptex->width);
792                 mtheight = LittleLong(dmiptex->height);
793                 mtdata = NULL;
794                 j = LittleLong(dmiptex->offsets[0]);
795                 if (j)
796                 {
797                         // texture included
798                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
799                         {
800                                 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
801                                 continue;
802                         }
803                         mtdata = (qbyte *)dmiptex + j;
804                 }
805
806                 if ((mtwidth & 15) || (mtheight & 15))
807                         Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
808
809                 // LordHavoc: force all names to lowercase
810                 for (j = 0;name[j];j++)
811                         if (name[j] >= 'A' && name[j] <= 'Z')
812                                 name[j] += 'a' - 'A';
813
814                 tx = loadmodel->brushq1.textures + i;
815                 strcpy(tx->name, name);
816                 tx->width = mtwidth;
817                 tx->height = mtheight;
818
819                 if (!tx->name[0])
820                 {
821                         sprintf(tx->name, "unnamed%i", i);
822                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
823                 }
824
825                 // LordHavoc: HL sky textures are entirely different than quake
826                 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
827                 {
828                         if (loadmodel->isworldmodel)
829                         {
830                                 data = loadimagepixels(tx->name, false, 0, 0);
831                                 if (data)
832                                 {
833                                         if (image_width == 256 && image_height == 128)
834                                         {
835                                                 R_InitSky(data, 4);
836                                                 Mem_Free(data);
837                                         }
838                                         else
839                                         {
840                                                 Mem_Free(data);
841                                                 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
842                                                 if (mtdata != NULL)
843                                                         R_InitSky(mtdata, 1);
844                                         }
845                                 }
846                                 else if (mtdata != NULL)
847                                         R_InitSky(mtdata, 1);
848                         }
849                 }
850                 else
851                 {
852                         if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
853                         {
854                                 // did not find external texture, load it from the bsp or wad3
855                                 if (loadmodel->brush.ishlbsp)
856                                 {
857                                         // internal texture overrides wad
858                                         qbyte *pixels, *freepixels, *fogpixels;
859                                         pixels = freepixels = NULL;
860                                         if (mtdata)
861                                                 pixels = W_ConvertWAD3Texture(dmiptex);
862                                         if (pixels == NULL)
863                                                 pixels = freepixels = W_GetTexture(tx->name);
864                                         if (pixels != NULL)
865                                         {
866                                                 tx->width = image_width;
867                                                 tx->height = image_height;
868                                                 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);
869                                                 if (Image_CheckAlpha(pixels, image_width * image_height, true))
870                                                 {
871                                                         fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
872                                                         for (j = 0;j < image_width * image_height * 4;j += 4)
873                                                         {
874                                                                 fogpixels[j + 0] = 255;
875                                                                 fogpixels[j + 1] = 255;
876                                                                 fogpixels[j + 2] = 255;
877                                                                 fogpixels[j + 3] = pixels[j + 3];
878                                                         }
879                                                         tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
880                                                         Mem_Free(fogpixels);
881                                                 }
882                                         }
883                                         if (freepixels)
884                                                 Mem_Free(freepixels);
885                                 }
886                                 else if (mtdata) // texture included
887                                         Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
888                         }
889                 }
890                 if (tx->skin.base == NULL)
891                 {
892                         // no texture found
893                         tx->width = 16;
894                         tx->height = 16;
895                         tx->skin.base = r_notexture;
896                 }
897
898                 if (tx->name[0] == '*')
899                 {
900                         // turb does not block movement
901                         tx->flags &= ~SURF_SOLIDCLIP;
902                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
903                         // LordHavoc: some turbulent textures should be fullbright and solid
904                         if (!strncmp(tx->name,"*lava",5)
905                          || !strncmp(tx->name,"*teleport",9)
906                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
907                                 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
908                         else
909                                 tx->flags |= SURF_WATERALPHA;
910                         tx->shader = &Cshader_water;
911                 }
912                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
913                 {
914                         tx->flags |= SURF_DRAWSKY;
915                         tx->shader = &Cshader_sky;
916                 }
917                 else
918                 {
919                         tx->flags |= SURF_LIGHTMAP;
920                         if (!tx->skin.fog)
921                                 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
922                         tx->shader = &Cshader_wall_lightmap;
923                 }
924
925                 // start out with no animation
926                 tx->currentframe = tx;
927         }
928
929         // sequence the animations
930         for (i = 0;i < m->nummiptex;i++)
931         {
932                 tx = loadmodel->brushq1.textures + i;
933                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
934                         continue;
935                 if (tx->anim_total[0] || tx->anim_total[1])
936                         continue;       // already sequenced
937
938                 // find the number of frames in the animation
939                 memset(anims, 0, sizeof(anims));
940                 memset(altanims, 0, sizeof(altanims));
941
942                 for (j = i;j < m->nummiptex;j++)
943                 {
944                         tx2 = loadmodel->brushq1.textures + j;
945                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
946                                 continue;
947
948                         num = tx2->name[1];
949                         if (num >= '0' && num <= '9')
950                                 anims[num - '0'] = tx2;
951                         else if (num >= 'a' && num <= 'j')
952                                 altanims[num - 'a'] = tx2;
953                         else
954                                 Con_Printf("Bad animating texture %s\n", tx->name);
955                 }
956
957                 max = altmax = 0;
958                 for (j = 0;j < 10;j++)
959                 {
960                         if (anims[j])
961                                 max = j + 1;
962                         if (altanims[j])
963                                 altmax = j + 1;
964                 }
965                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
966
967                 incomplete = false;
968                 for (j = 0;j < max;j++)
969                 {
970                         if (!anims[j])
971                         {
972                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
973                                 incomplete = true;
974                         }
975                 }
976                 for (j = 0;j < altmax;j++)
977                 {
978                         if (!altanims[j])
979                         {
980                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
981                                 incomplete = true;
982                         }
983                 }
984                 if (incomplete)
985                         continue;
986
987                 if (altmax < 1)
988                 {
989                         // if there is no alternate animation, duplicate the primary
990                         // animation into the alternate
991                         altmax = max;
992                         for (k = 0;k < 10;k++)
993                                 altanims[k] = anims[k];
994                 }
995
996                 // link together the primary animation
997                 for (j = 0;j < max;j++)
998                 {
999                         tx2 = anims[j];
1000                         tx2->animated = true;
1001                         tx2->anim_total[0] = max;
1002                         tx2->anim_total[1] = altmax;
1003                         for (k = 0;k < 10;k++)
1004                         {
1005                                 tx2->anim_frames[0][k] = anims[k];
1006                                 tx2->anim_frames[1][k] = altanims[k];
1007                         }
1008                 }
1009
1010                 // if there really is an alternate anim...
1011                 if (anims[0] != altanims[0])
1012                 {
1013                         // link together the alternate animation
1014                         for (j = 0;j < altmax;j++)
1015                         {
1016                                 tx2 = altanims[j];
1017                                 tx2->animated = true;
1018                                 // the primary/alternate are reversed here
1019                                 tx2->anim_total[0] = altmax;
1020                                 tx2->anim_total[1] = max;
1021                                 for (k = 0;k < 10;k++)
1022                                 {
1023                                         tx2->anim_frames[0][k] = altanims[k];
1024                                         tx2->anim_frames[1][k] = anims[k];
1025                                 }
1026                         }
1027                 }
1028         }
1029 }
1030
1031 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1032 {
1033         int i;
1034         qbyte *in, *out, *data, d;
1035         char litfilename[1024];
1036         loadmodel->brushq1.lightdata = NULL;
1037         if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1038         {
1039                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1040                 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1041         }
1042         else // LordHavoc: bsp version 29 (normal white lighting)
1043         {
1044                 // LordHavoc: hope is not lost yet, check for a .lit file to load
1045                 strcpy(litfilename, loadmodel->name);
1046                 FS_StripExtension(litfilename, litfilename);
1047                 strcat(litfilename, ".lit");
1048                 data = (qbyte*) FS_LoadFile(litfilename, false);
1049                 if (data)
1050                 {
1051                         if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1052                         {
1053                                 i = LittleLong(((int *)data)[1]);
1054                                 if (i == 1)
1055                                 {
1056                                         Con_DPrintf("loaded %s\n", litfilename);
1057                                         loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1058                                         memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1059                                         Mem_Free(data);
1060                                         return;
1061                                 }
1062                                 else
1063                                 {
1064                                         Con_Printf("Unknown .lit file version (%d)\n", i);
1065                                         Mem_Free(data);
1066                                 }
1067                         }
1068                         else
1069                         {
1070                                 if (fs_filesize == 8)
1071                                         Con_Printf("Empty .lit file, ignoring\n");
1072                                 else
1073                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1074                                 Mem_Free(data);
1075                         }
1076                 }
1077                 // LordHavoc: oh well, expand the white lighting data
1078                 if (!l->filelen)
1079                         return;
1080                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1081                 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1082                 out = loadmodel->brushq1.lightdata;
1083                 memcpy(in, mod_base + l->fileofs, l->filelen);
1084                 for (i = 0;i < l->filelen;i++)
1085                 {
1086                         d = *in++;
1087                         *out++ = d;
1088                         *out++ = d;
1089                         *out++ = d;
1090                 }
1091         }
1092 }
1093
1094 static void Mod_Q1BSP_LoadLightList(void)
1095 {
1096         int a, n, numlights;
1097         char lightsfilename[1024], *s, *t, *lightsstring;
1098         mlight_t *e;
1099
1100         strcpy(lightsfilename, loadmodel->name);
1101         FS_StripExtension(lightsfilename, lightsfilename);
1102         strcat(lightsfilename, ".lights");
1103         s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1104         if (s)
1105         {
1106                 numlights = 0;
1107                 while (*s)
1108                 {
1109                         while (*s && *s != '\n')
1110                                 s++;
1111                         if (!*s)
1112                         {
1113                                 Mem_Free(lightsstring);
1114                                 Host_Error("lights file must end with a newline\n");
1115                         }
1116                         s++;
1117                         numlights++;
1118                 }
1119                 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1120                 s = lightsstring;
1121                 n = 0;
1122                 while (*s && n < numlights)
1123                 {
1124                         t = s;
1125                         while (*s && *s != '\n')
1126                                 s++;
1127                         if (!*s)
1128                         {
1129                                 Mem_Free(lightsstring);
1130                                 Host_Error("misparsed lights file!\n");
1131                         }
1132                         e = loadmodel->brushq1.lights + n;
1133                         *s = 0;
1134                         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);
1135                         *s = '\n';
1136                         if (a != 14)
1137                         {
1138                                 Mem_Free(lightsstring);
1139                                 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);
1140                         }
1141                         s++;
1142                         n++;
1143                 }
1144                 if (*s)
1145                 {
1146                         Mem_Free(lightsstring);
1147                         Host_Error("misparsed lights file!\n");
1148                 }
1149                 loadmodel->brushq1.numlights = numlights;
1150                 Mem_Free(lightsstring);
1151         }
1152 }
1153
1154 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1155 {
1156         loadmodel->brushq1.num_compressedpvs = 0;
1157         loadmodel->brushq1.data_compressedpvs = NULL;
1158         if (!l->filelen)
1159                 return;
1160         loadmodel->brushq1.num_compressedpvs = l->filelen;
1161         loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1162         memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1163 }
1164
1165 // used only for HalfLife maps
1166 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1167 {
1168         char key[128], value[4096];
1169         char wadname[128];
1170         int i, j, k;
1171         if (!data)
1172                 return;
1173         if (!COM_ParseToken(&data, false))
1174                 return; // error
1175         if (com_token[0] != '{')
1176                 return; // error
1177         while (1)
1178         {
1179                 if (!COM_ParseToken(&data, false))
1180                         return; // error
1181                 if (com_token[0] == '}')
1182                         break; // end of worldspawn
1183                 if (com_token[0] == '_')
1184                         strcpy(key, com_token + 1);
1185                 else
1186                         strcpy(key, com_token);
1187                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1188                         key[strlen(key)-1] = 0;
1189                 if (!COM_ParseToken(&data, false))
1190                         return; // error
1191                 strcpy(value, com_token);
1192                 if (!strcmp("wad", key)) // for HalfLife maps
1193                 {
1194                         if (loadmodel->brush.ishlbsp)
1195                         {
1196                                 j = 0;
1197                                 for (i = 0;i < 4096;i++)
1198                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1199                                                 break;
1200                                 if (value[i])
1201                                 {
1202                                         for (;i < 4096;i++)
1203                                         {
1204                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1205                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1206                                                         j = i+1;
1207                                                 else if (value[i] == ';' || value[i] == 0)
1208                                                 {
1209                                                         k = value[i];
1210                                                         value[i] = 0;
1211                                                         strcpy(wadname, "textures/");
1212                                                         strcat(wadname, &value[j]);
1213                                                         W_LoadTextureWadFile(wadname, false);
1214                                                         j = i+1;
1215                                                         if (!k)
1216                                                                 break;
1217                                                 }
1218                                         }
1219                                 }
1220                         }
1221                 }
1222         }
1223 }
1224
1225 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1226 {
1227         loadmodel->brush.entities = NULL;
1228         if (!l->filelen)
1229                 return;
1230         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1231         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1232         if (loadmodel->brush.ishlbsp)
1233                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1234 }
1235
1236
1237 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1238 {
1239         dvertex_t       *in;
1240         mvertex_t       *out;
1241         int                     i, count;
1242
1243         in = (void *)(mod_base + l->fileofs);
1244         if (l->filelen % sizeof(*in))
1245                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1246         count = l->filelen / sizeof(*in);
1247         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1248
1249         loadmodel->brushq1.vertexes = out;
1250         loadmodel->brushq1.numvertexes = count;
1251
1252         for ( i=0 ; i<count ; i++, in++, out++)
1253         {
1254                 out->position[0] = LittleFloat(in->point[0]);
1255                 out->position[1] = LittleFloat(in->point[1]);
1256                 out->position[2] = LittleFloat(in->point[2]);
1257         }
1258 }
1259
1260 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1261 {
1262         dmodel_t        *in;
1263         dmodel_t        *out;
1264         int                     i, j, count;
1265
1266         in = (void *)(mod_base + l->fileofs);
1267         if (l->filelen % sizeof(*in))
1268                 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1269         count = l->filelen / sizeof(*in);
1270         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1271
1272         loadmodel->brushq1.submodels = out;
1273         loadmodel->brush.numsubmodels = count;
1274
1275         for ( i=0 ; i<count ; i++, in++, out++)
1276         {
1277                 for (j=0 ; j<3 ; j++)
1278                 {
1279                         // spread the mins / maxs by a pixel
1280                         out->mins[j] = LittleFloat(in->mins[j]) - 1;
1281                         out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1282                         out->origin[j] = LittleFloat(in->origin[j]);
1283                 }
1284                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1285                         out->headnode[j] = LittleLong(in->headnode[j]);
1286                 out->visleafs = LittleLong(in->visleafs);
1287                 out->firstface = LittleLong(in->firstface);
1288                 out->numfaces = LittleLong(in->numfaces);
1289         }
1290 }
1291
1292 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1293 {
1294         dedge_t *in;
1295         medge_t *out;
1296         int     i, count;
1297
1298         in = (void *)(mod_base + l->fileofs);
1299         if (l->filelen % sizeof(*in))
1300                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1301         count = l->filelen / sizeof(*in);
1302         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1303
1304         loadmodel->brushq1.edges = out;
1305         loadmodel->brushq1.numedges = count;
1306
1307         for ( i=0 ; i<count ; i++, in++, out++)
1308         {
1309                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1310                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1311         }
1312 }
1313
1314 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1315 {
1316         texinfo_t *in;
1317         mtexinfo_t *out;
1318         int i, j, k, count, miptex;
1319
1320         in = (void *)(mod_base + l->fileofs);
1321         if (l->filelen % sizeof(*in))
1322                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1323         count = l->filelen / sizeof(*in);
1324         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1325
1326         loadmodel->brushq1.texinfo = out;
1327         loadmodel->brushq1.numtexinfo = count;
1328
1329         for (i = 0;i < count;i++, in++, out++)
1330         {
1331                 for (k = 0;k < 2;k++)
1332                         for (j = 0;j < 4;j++)
1333                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1334
1335                 miptex = LittleLong(in->miptex);
1336                 out->flags = LittleLong(in->flags);
1337
1338                 out->texture = NULL;
1339                 if (loadmodel->brushq1.textures)
1340                 {
1341                         if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1342                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1343                         else
1344                                 out->texture = loadmodel->brushq1.textures + miptex;
1345                 }
1346                 if (out->flags & TEX_SPECIAL)
1347                 {
1348                         // if texture chosen is NULL or the shader needs a lightmap,
1349                         // force to notexture water shader
1350                         if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1351                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1352                 }
1353                 else
1354                 {
1355                         // if texture chosen is NULL, force to notexture
1356                         if (out->texture == NULL)
1357                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1358                 }
1359         }
1360 }
1361
1362 #if 0
1363 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1364 {
1365         int             i, j;
1366         float   *v;
1367
1368         mins[0] = mins[1] = mins[2] = 9999;
1369         maxs[0] = maxs[1] = maxs[2] = -9999;
1370         v = verts;
1371         for (i = 0;i < numverts;i++)
1372         {
1373                 for (j = 0;j < 3;j++, v++)
1374                 {
1375                         if (*v < mins[j])
1376                                 mins[j] = *v;
1377                         if (*v > maxs[j])
1378                                 maxs[j] = *v;
1379                 }
1380         }
1381 }
1382
1383 #define MAX_SUBDIVPOLYTRIANGLES 4096
1384 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1385
1386 static int subdivpolyverts, subdivpolytriangles;
1387 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1388 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1389
1390 static int subdivpolylookupvert(vec3_t v)
1391 {
1392         int i;
1393         for (i = 0;i < subdivpolyverts;i++)
1394                 if (subdivpolyvert[i][0] == v[0]
1395                  && subdivpolyvert[i][1] == v[1]
1396                  && subdivpolyvert[i][2] == v[2])
1397                         return i;
1398         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1399                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1400         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1401         return subdivpolyverts++;
1402 }
1403
1404 static void SubdividePolygon(int numverts, float *verts)
1405 {
1406         int             i, i1, i2, i3, f, b, c, p;
1407         vec3_t  mins, maxs, front[256], back[256];
1408         float   m, *pv, *cv, dist[256], frac;
1409
1410         if (numverts > 250)
1411                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1412
1413         BoundPoly(numverts, verts, mins, maxs);
1414
1415         for (i = 0;i < 3;i++)
1416         {
1417                 m = (mins[i] + maxs[i]) * 0.5;
1418                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1419                 if (maxs[i] - m < 8)
1420                         continue;
1421                 if (m - mins[i] < 8)
1422                         continue;
1423
1424                 // cut it
1425                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1426                         dist[c] = cv[i] - m;
1427
1428                 f = b = 0;
1429                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1430                 {
1431                         if (dist[p] >= 0)
1432                         {
1433                                 VectorCopy(pv, front[f]);
1434                                 f++;
1435                         }
1436                         if (dist[p] <= 0)
1437                         {
1438                                 VectorCopy(pv, back[b]);
1439                                 b++;
1440                         }
1441                         if (dist[p] == 0 || dist[c] == 0)
1442                                 continue;
1443                         if ((dist[p] > 0) != (dist[c] > 0) )
1444                         {
1445                                 // clip point
1446                                 frac = dist[p] / (dist[p] - dist[c]);
1447                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1448                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1449                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1450                                 f++;
1451                                 b++;
1452                         }
1453                 }
1454
1455                 SubdividePolygon(f, front[0]);
1456                 SubdividePolygon(b, back[0]);
1457                 return;
1458         }
1459
1460         i1 = subdivpolylookupvert(verts);
1461         i2 = subdivpolylookupvert(verts + 3);
1462         for (i = 2;i < numverts;i++)
1463         {
1464                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1465                 {
1466                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1467                         return;
1468                 }
1469
1470                 i3 = subdivpolylookupvert(verts + i * 3);
1471                 subdivpolyindex[subdivpolytriangles][0] = i1;
1472                 subdivpolyindex[subdivpolytriangles][1] = i2;
1473                 subdivpolyindex[subdivpolytriangles][2] = i3;
1474                 i2 = i3;
1475                 subdivpolytriangles++;
1476         }
1477 }
1478
1479 //Breaks a polygon up along axial 64 unit
1480 //boundaries so that turbulent and sky warps
1481 //can be done reasonably.
1482 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1483 {
1484         int i, j;
1485         surfvertex_t *v;
1486         surfmesh_t *mesh;
1487
1488         subdivpolytriangles = 0;
1489         subdivpolyverts = 0;
1490         SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1491         if (subdivpolytriangles < 1)
1492                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1493
1494         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1495         mesh->numverts = subdivpolyverts;
1496         mesh->numtriangles = subdivpolytriangles;
1497         mesh->vertex = (surfvertex_t *)(mesh + 1);
1498         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1499         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1500
1501         for (i = 0;i < mesh->numtriangles;i++)
1502                 for (j = 0;j < 3;j++)
1503                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1504
1505         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1506         {
1507                 VectorCopy(subdivpolyvert[i], v->v);
1508                 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1509                 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1510         }
1511 }
1512 #endif
1513
1514 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1515 {
1516         surfmesh_t *mesh;
1517         mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1518         mesh->numverts = numverts;
1519         mesh->numtriangles = numtriangles;
1520         mesh->vertex3f = (float *)(mesh + 1);
1521         mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1522         mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1523         mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1524         mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1525         mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1526         mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1527         mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1528         mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1529         mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1530         return mesh;
1531 }
1532
1533 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1534 {
1535         int i, lindex, j;
1536         float *vec, *vert, mins[3], maxs[3], val, *v;
1537         mtexinfo_t *tex;
1538
1539         // convert edges back to a normal polygon
1540         surf->poly_numverts = numedges;
1541         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1542         for (i = 0;i < numedges;i++)
1543         {
1544                 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1545                 if (lindex > 0)
1546                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1547                 else
1548                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1549                 VectorCopy(vec, vert);
1550                 vert += 3;
1551         }
1552
1553         // calculate polygon bounding box and center
1554         vert = surf->poly_verts;
1555         VectorCopy(vert, mins);
1556         VectorCopy(vert, maxs);
1557         vert += 3;
1558         for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1559         {
1560                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1561                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1562                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1563         }
1564         VectorCopy(mins, surf->poly_mins);
1565         VectorCopy(maxs, surf->poly_maxs);
1566         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1567         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1568         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1569
1570         // generate surface extents information
1571         tex = surf->texinfo;
1572         mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1573         mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1574         for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1575         {
1576                 for (j = 0;j < 2;j++)
1577                 {
1578                         val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1579                         if (mins[j] > val)
1580                                 mins[j] = val;
1581                         if (maxs[j] < val)
1582                                 maxs[j] = val;
1583                 }
1584         }
1585         for (i = 0;i < 2;i++)
1586         {
1587                 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1588                 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1589         }
1590 }
1591
1592 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1593 {
1594         dface_t *in;
1595         msurface_t *surf;
1596         int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1597         surfmesh_t *mesh;
1598         float s, t;
1599
1600         in = (void *)(mod_base + l->fileofs);
1601         if (l->filelen % sizeof(*in))
1602                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1603         count = l->filelen / sizeof(*in);
1604         loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1605
1606         loadmodel->brushq1.numsurfaces = count;
1607         loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1608         loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1609         loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1610
1611         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++)
1612         {
1613                 surf->number = surfnum;
1614                 // FIXME: validate edges, texinfo, etc?
1615                 firstedge = LittleLong(in->firstedge);
1616                 numedges = LittleShort(in->numedges);
1617                 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)
1618                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1619                 i = LittleShort(in->texinfo);
1620                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1621                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1622                 surf->texinfo = loadmodel->brushq1.texinfo + i;
1623                 surf->flags = surf->texinfo->texture->flags;
1624
1625                 planenum = LittleShort(in->planenum);
1626                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1627                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1628
1629                 if (LittleShort(in->side))
1630                         surf->flags |= SURF_PLANEBACK;
1631
1632                 surf->plane = loadmodel->brushq1.planes + planenum;
1633
1634                 // clear lightmap (filled in later)
1635                 surf->lightmaptexture = NULL;
1636
1637                 // force lightmap upload on first time seeing the surface
1638                 surf->cached_dlight = true;
1639
1640                 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1641
1642                 ssize = (surf->extents[0] >> 4) + 1;
1643                 tsize = (surf->extents[1] >> 4) + 1;
1644
1645                 // lighting info
1646                 for (i = 0;i < MAXLIGHTMAPS;i++)
1647                         surf->styles[i] = in->styles[i];
1648                 i = LittleLong(in->lightofs);
1649                 if (i == -1)
1650                         surf->samples = NULL;
1651                 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1652                         surf->samples = loadmodel->brushq1.lightdata + i;
1653                 else // LordHavoc: white lighting (bsp version 29)
1654                         surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1655
1656                 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1657                 {
1658                         if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1659                                 Host_Error("Bad surface extents");
1660                         // stainmap for permanent marks on walls
1661                         surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1662                         // clear to white
1663                         memset(surf->stainsamples, 255, ssize * tsize * 3);
1664                 }
1665         }
1666
1667         loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1668         loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
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 = loadmodel->brushq1.surfmeshes + totalmeshes;
1673                 mesh->numverts = surf->poly_numverts;
1674                 mesh->numtriangles = surf->poly_numverts - 2;
1675                 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1676                 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1677                 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1678                 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1679                 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1680                 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1681                 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1682                 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1683                 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1684                 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1685
1686                 surf->lightmaptexturestride = 0;
1687                 surf->lightmaptexture = NULL;
1688
1689                 for (i = 0;i < mesh->numverts;i++)
1690                 {
1691                         mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1692                         mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1693                         mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1694                         s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1695                         t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1696                         mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1697                         mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1698                         mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1699                         mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1700                         mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1701                         mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1702                         mesh->lightmapoffsets[i] = 0;
1703                 }
1704
1705                 for (i = 0;i < mesh->numtriangles;i++)
1706                 {
1707                         mesh->element3i[i * 3 + 0] = 0;
1708                         mesh->element3i[i * 3 + 1] = i + 1;
1709                         mesh->element3i[i * 3 + 2] = i + 2;
1710                 }
1711
1712                 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1713                 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->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->numverts;i++)
1739                         {
1740                                 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1741                                 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1742                                 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1743                                 mesh->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->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         surfmesh_t *mesh;
2659
2660         mod->type = mod_brush;
2661
2662         header = (dheader_t *)buffer;
2663
2664         i = LittleLong(header->version);
2665         if (i != BSPVERSION && i != 30)
2666                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2667         mod->brush.ishlbsp = i == 30;
2668
2669         mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2670         mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2671         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2672         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2673         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2674         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2675         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2676         mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
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->DrawFakeShadow = NULL;
2756                 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2757                 mod->DrawLight = R_Model_Brush_DrawLight;
2758                 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2759                 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2760                 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2761                 Mod_Q1BSP_BuildPVSTextureChains(mod);
2762                 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2763                 if (mod->brushq1.nummodelsurfaces)
2764                 {
2765                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2766                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2767                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2768                         modelyawradius = 0;
2769                         modelradius = 0;
2770                         for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2771                         {
2772                                 // we only need to have a drawsky function if it is used(usually only on world model)
2773                                 if (surf->texinfo->texture->shader == &Cshader_sky)
2774                                         mod->DrawSky = R_Model_Brush_DrawSky;
2775                                 // LordHavoc: submodels always clip, even if water
2776                                 if (mod->brush.numsubmodels - 1)
2777                                         surf->flags |= SURF_SOLIDCLIP;
2778                                 // calculate bounding shapes
2779                                 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2780                                 {
2781                                         for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2782                                         {
2783                                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2784                                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2785                                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2786                                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2787                                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2788                                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2789                                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2790                                                 if (modelyawradius < dist)
2791                                                         modelyawradius = dist;
2792                                                 dist += vec[2]*vec[2];
2793                                                 if (modelradius < dist)
2794                                                         modelradius = dist;
2795                                         }
2796                                 }
2797                         }
2798                         modelyawradius = sqrt(modelyawradius);
2799                         modelradius = sqrt(modelradius);
2800                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2801                         mod->yawmins[2] = mod->normalmins[2];
2802                         mod->yawmaxs[2] = mod->normalmaxs[2];
2803                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2804                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2805                         mod->radius = modelradius;
2806                         mod->radius2 = modelradius * modelradius;
2807                 }
2808                 else
2809                 {
2810                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2811                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2812                 }
2813                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2814
2815                 mod->brushq1.visleafs = bm->visleafs;
2816
2817                 // LordHavoc: only register submodels if it is the world
2818                 // (prevents bsp models from replacing world submodels)
2819                 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2820                 {
2821                         char    name[10];
2822                         // duplicate the basic information
2823                         sprintf(name, "*%i", i+1);
2824                         loadmodel = Mod_FindName(name);
2825                         *loadmodel = *mod;
2826                         strcpy(loadmodel->name, name);
2827                         // textures and memory belong to the main model
2828                         loadmodel->texturepool = NULL;
2829                         loadmodel->mempool = NULL;
2830                         mod = loadmodel;
2831                 }
2832         }
2833
2834         loadmodel = originalloadmodel;
2835         //Mod_Q1BSP_ProcessLightList();
2836 }
2837
2838 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2839 {
2840 }
2841
2842 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2843 {
2844 /*
2845         d_t *in;
2846         m_t *out;
2847         int i, count;
2848
2849         in = (void *)(mod_base + l->fileofs);
2850         if (l->filelen % sizeof(*in))
2851                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2852         count = l->filelen / sizeof(*in);
2853         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2854
2855         loadmodel-> = out;
2856         loadmodel->num = count;
2857
2858         for (i = 0;i < count;i++, in++, out++)
2859         {
2860         }
2861 */
2862 }
2863
2864 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2865 {
2866 /*
2867         d_t *in;
2868         m_t *out;
2869         int i, count;
2870
2871         in = (void *)(mod_base + l->fileofs);
2872         if (l->filelen % sizeof(*in))
2873                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2874         count = l->filelen / sizeof(*in);
2875         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2876
2877         loadmodel-> = out;
2878         loadmodel->num = count;
2879
2880         for (i = 0;i < count;i++, in++, out++)
2881         {
2882         }
2883 */
2884 }
2885
2886 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2887 {
2888 /*
2889         d_t *in;
2890         m_t *out;
2891         int i, count;
2892
2893         in = (void *)(mod_base + l->fileofs);
2894         if (l->filelen % sizeof(*in))
2895                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2896         count = l->filelen / sizeof(*in);
2897         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2898
2899         loadmodel-> = out;
2900         loadmodel->num = count;
2901
2902         for (i = 0;i < count;i++, in++, out++)
2903         {
2904         }
2905 */
2906 }
2907
2908 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2909 {
2910 /*
2911         d_t *in;
2912         m_t *out;
2913         int i, count;
2914
2915         in = (void *)(mod_base + l->fileofs);
2916         if (l->filelen % sizeof(*in))
2917                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2918         count = l->filelen / sizeof(*in);
2919         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2920
2921         loadmodel-> = out;
2922         loadmodel->num = count;
2923
2924         for (i = 0;i < count;i++, in++, out++)
2925         {
2926         }
2927 */
2928 }
2929
2930 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2931 {
2932 /*
2933         d_t *in;
2934         m_t *out;
2935         int i, count;
2936
2937         in = (void *)(mod_base + l->fileofs);
2938         if (l->filelen % sizeof(*in))
2939                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2940         count = l->filelen / sizeof(*in);
2941         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2942
2943         loadmodel-> = out;
2944         loadmodel->num = count;
2945
2946         for (i = 0;i < count;i++, in++, out++)
2947         {
2948         }
2949 */
2950 }
2951
2952 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2953 {
2954 /*
2955         d_t *in;
2956         m_t *out;
2957         int i, count;
2958
2959         in = (void *)(mod_base + l->fileofs);
2960         if (l->filelen % sizeof(*in))
2961                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2962         count = l->filelen / sizeof(*in);
2963         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2964
2965         loadmodel-> = out;
2966         loadmodel->num = count;
2967
2968         for (i = 0;i < count;i++, in++, out++)
2969         {
2970         }
2971 */
2972 }
2973
2974 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2975 {
2976 /*
2977         d_t *in;
2978         m_t *out;
2979         int i, count;
2980
2981         in = (void *)(mod_base + l->fileofs);
2982         if (l->filelen % sizeof(*in))
2983                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2984         count = l->filelen / sizeof(*in);
2985         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2986
2987         loadmodel-> = out;
2988         loadmodel->num = count;
2989
2990         for (i = 0;i < count;i++, in++, out++)
2991         {
2992         }
2993 */
2994 }
2995
2996 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
2997 {
2998 /*
2999         d_t *in;
3000         m_t *out;
3001         int i, count;
3002
3003         in = (void *)(mod_base + l->fileofs);
3004         if (l->filelen % sizeof(*in))
3005                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3006         count = l->filelen / sizeof(*in);
3007         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3008
3009         loadmodel-> = out;
3010         loadmodel->num = count;
3011
3012         for (i = 0;i < count;i++, in++, out++)
3013         {
3014         }
3015 */
3016 }
3017
3018 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3019 {
3020 /*
3021         d_t *in;
3022         m_t *out;
3023         int i, count;
3024
3025         in = (void *)(mod_base + l->fileofs);
3026         if (l->filelen % sizeof(*in))
3027                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3028         count = l->filelen / sizeof(*in);
3029         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3030
3031         loadmodel-> = out;
3032         loadmodel->num = count;
3033
3034         for (i = 0;i < count;i++, in++, out++)
3035         {
3036         }
3037 */
3038 }
3039
3040 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3041 {
3042 /*
3043         d_t *in;
3044         m_t *out;
3045         int i, count;
3046
3047         in = (void *)(mod_base + l->fileofs);
3048         if (l->filelen % sizeof(*in))
3049                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3050         count = l->filelen / sizeof(*in);
3051         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3052
3053         loadmodel-> = out;
3054         loadmodel->num = count;
3055
3056         for (i = 0;i < count;i++, in++, out++)
3057         {
3058         }
3059 */
3060 }
3061
3062 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3063 {
3064 /*
3065         d_t *in;
3066         m_t *out;
3067         int i, count;
3068
3069         in = (void *)(mod_base + l->fileofs);
3070         if (l->filelen % sizeof(*in))
3071                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3072         count = l->filelen / sizeof(*in);
3073         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3074
3075         loadmodel-> = out;
3076         loadmodel->num = count;
3077
3078         for (i = 0;i < count;i++, in++, out++)
3079         {
3080         }
3081 */
3082 }
3083
3084 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3085 {
3086 /*
3087         d_t *in;
3088         m_t *out;
3089         int i, count;
3090
3091         in = (void *)(mod_base + l->fileofs);
3092         if (l->filelen % sizeof(*in))
3093                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3094         count = l->filelen / sizeof(*in);
3095         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3096
3097         loadmodel-> = out;
3098         loadmodel->num = count;
3099
3100         for (i = 0;i < count;i++, in++, out++)
3101         {
3102         }
3103 */
3104 }
3105
3106 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3107 {
3108 /*
3109         d_t *in;
3110         m_t *out;
3111         int i, count;
3112
3113         in = (void *)(mod_base + l->fileofs);
3114         if (l->filelen % sizeof(*in))
3115                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3116         count = l->filelen / sizeof(*in);
3117         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3118
3119         loadmodel-> = out;
3120         loadmodel->num = count;
3121
3122         for (i = 0;i < count;i++, in++, out++)
3123         {
3124         }
3125 */
3126 }
3127
3128 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3129 {
3130 /*
3131         d_t *in;
3132         m_t *out;
3133         int i, count;
3134
3135         in = (void *)(mod_base + l->fileofs);
3136         if (l->filelen % sizeof(*in))
3137                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3138         count = l->filelen / sizeof(*in);
3139         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3140
3141         loadmodel-> = out;
3142         loadmodel->num = count;
3143
3144         for (i = 0;i < count;i++, in++, out++)
3145         {
3146         }
3147 */
3148 }
3149
3150 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3151 {
3152 /*
3153         d_t *in;
3154         m_t *out;
3155         int i, count;
3156
3157         in = (void *)(mod_base + l->fileofs);
3158         if (l->filelen % sizeof(*in))
3159                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3160         count = l->filelen / sizeof(*in);
3161         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3162
3163         loadmodel-> = out;
3164         loadmodel->num = count;
3165
3166         for (i = 0;i < count;i++, in++, out++)
3167         {
3168         }
3169 */
3170 }
3171
3172 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3173 {
3174 /*
3175         d_t *in;
3176         m_t *out;
3177         int i, count;
3178
3179         in = (void *)(mod_base + l->fileofs);
3180         if (l->filelen % sizeof(*in))
3181                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3182         count = l->filelen / sizeof(*in);
3183         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3184
3185         loadmodel-> = out;
3186         loadmodel->num = count;
3187
3188         for (i = 0;i < count;i++, in++, out++)
3189         {
3190         }
3191 */
3192 }
3193
3194 static void Mod_Q2BSP_LoadModels(lump_t *l)
3195 {
3196 /*
3197         d_t *in;
3198         m_t *out;
3199         int i, count;
3200
3201         in = (void *)(mod_base + l->fileofs);
3202         if (l->filelen % sizeof(*in))
3203                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3204         count = l->filelen / sizeof(*in);
3205         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3206
3207         loadmodel-> = out;
3208         loadmodel->num = count;
3209
3210         for (i = 0;i < count;i++, in++, out++)
3211         {
3212         }
3213 */
3214 }
3215
3216 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3217 {
3218         int i;
3219         q2dheader_t *header;
3220
3221         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3222
3223         mod->type = mod_brushq2;
3224
3225         header = (q2dheader_t *)buffer;
3226
3227         i = LittleLong(header->version);
3228         if (i != Q2BSPVERSION)
3229                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3230         mod->brush.ishlbsp = false;
3231         if (loadmodel->isworldmodel)
3232         {
3233                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3234                 // until we get a texture for it...
3235                 R_ResetQuakeSky();
3236         }
3237
3238         mod_base = (qbyte *)header;
3239
3240         // swap all the lumps
3241         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3242                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3243
3244         // store which lightmap format to use
3245         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3246
3247         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3248         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3249         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3250         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3251         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3252         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3253         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3254         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3255         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3256         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3257         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3258         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3259         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3260         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3261         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3262         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3263         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3264         // LordHavoc: must go last because this makes the submodels
3265         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3266 }
3267
3268 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3269 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3270
3271 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3272 {
3273         const char *data;
3274         char key[128], value[4096];
3275         float v[3];
3276         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3277         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3278         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3279         if (!l->filelen)
3280                 return;
3281         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3282         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3283         data = loadmodel->brush.entities;
3284         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3285         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3286         {
3287                 while (1)
3288                 {
3289                         if (!COM_ParseToken(&data, false))
3290                                 break; // error
3291                         if (com_token[0] == '}')
3292                                 break; // end of worldspawn
3293                         if (com_token[0] == '_')
3294                                 strcpy(key, com_token + 1);
3295                         else
3296                                 strcpy(key, com_token);
3297                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3298                                 key[strlen(key)-1] = 0;
3299                         if (!COM_ParseToken(&data, false))
3300                                 break; // error
3301                         strcpy(value, com_token);
3302                         if (!strcmp("gridsize", key))
3303                         {
3304                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3305                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3306                         }
3307                 }
3308         }
3309 }
3310
3311 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3312 {
3313         q3dtexture_t *in;
3314         q3mtexture_t *out;
3315         int i, count;
3316
3317         in = (void *)(mod_base + l->fileofs);
3318         if (l->filelen % sizeof(*in))
3319                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3320         count = l->filelen / sizeof(*in);
3321         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3322
3323         loadmodel->brushq3.data_textures = out;
3324         loadmodel->brushq3.num_textures = count;
3325
3326         for (i = 0;i < count;i++, in++, out++)
3327         {
3328                 strncpy(out->name, in->name, sizeof(out->name) - 1);
3329                 out->surfaceflags = LittleLong(in->surfaceflags);
3330                 out->nativecontents = LittleLong(in->contents);
3331                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3332                 out->renderflags = 0;
3333                 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3334                         out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3335                 if (!strncmp(out->name, "textures/skies/", 15))
3336                         out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY;
3337
3338                 out->number = i;
3339                 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3340         }
3341 }
3342
3343 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3344 {
3345         q3dplane_t *in;
3346         mplane_t *out;
3347         int i, count;
3348
3349         in = (void *)(mod_base + l->fileofs);
3350         if (l->filelen % sizeof(*in))
3351                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3352         count = l->filelen / sizeof(*in);
3353         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3354
3355         loadmodel->brushq3.data_planes = out;
3356         loadmodel->brushq3.num_planes = count;
3357
3358         for (i = 0;i < count;i++, in++, out++)
3359         {
3360                 out->normal[0] = LittleLong(in->normal[0]);
3361                 out->normal[1] = LittleLong(in->normal[1]);
3362                 out->normal[2] = LittleLong(in->normal[2]);
3363                 out->dist = LittleLong(in->dist);
3364                 PlaneClassify(out);
3365         }
3366 }
3367
3368 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3369 {
3370         q3dbrushside_t *in;
3371         q3mbrushside_t *out;
3372         int i, n, count;
3373
3374         in = (void *)(mod_base + l->fileofs);
3375         if (l->filelen % sizeof(*in))
3376                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3377         count = l->filelen / sizeof(*in);
3378         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3379
3380         loadmodel->brushq3.data_brushsides = out;
3381         loadmodel->brushq3.num_brushsides = count;
3382
3383         for (i = 0;i < count;i++, in++, out++)
3384         {
3385                 n = LittleLong(in->planeindex);
3386                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3387                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3388                 out->plane = loadmodel->brushq3.data_planes + n;
3389                 n = LittleLong(in->textureindex);
3390                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3391                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3392                 out->texture = loadmodel->brushq3.data_textures + n;
3393         }
3394 }
3395
3396 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3397 {
3398         q3dbrush_t *in;
3399         q3mbrush_t *out;
3400         int i, j, n, c, count, maxplanes;
3401         mplane_t *planes;
3402
3403         in = (void *)(mod_base + l->fileofs);
3404         if (l->filelen % sizeof(*in))
3405                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3406         count = l->filelen / sizeof(*in);
3407         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3408
3409         loadmodel->brushq3.data_brushes = out;
3410         loadmodel->brushq3.num_brushes = count;
3411
3412         maxplanes = 0;
3413         planes = NULL;
3414
3415         for (i = 0;i < count;i++, in++, out++)
3416         {
3417                 n = LittleLong(in->firstbrushside);
3418                 c = LittleLong(in->numbrushsides);
3419                 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3420                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3421                 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3422                 out->numbrushsides = c;
3423                 n = LittleLong(in->textureindex);
3424                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3425                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3426                 out->texture = loadmodel->brushq3.data_textures + n;
3427
3428                 // make a list of mplane_t structs to construct a colbrush from
3429                 if (maxplanes < out->numbrushsides)
3430                 {
3431                         maxplanes = out->numbrushsides;
3432                         if (planes)
3433                                 Mem_Free(planes);
3434                         planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3435                 }
3436                 for (j = 0;j < out->numbrushsides;j++)
3437                 {
3438                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3439                         planes[j].dist = out->firstbrushside[j].plane->dist;
3440                 }
3441                 // make the colbrush from the planes
3442                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3443         }
3444         if (planes)
3445                 Mem_Free(planes);
3446 }
3447
3448 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3449 {
3450         q3deffect_t *in;
3451         q3meffect_t *out;
3452         int i, n, count;
3453
3454         in = (void *)(mod_base + l->fileofs);
3455         if (l->filelen % sizeof(*in))
3456                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3457         count = l->filelen / sizeof(*in);
3458         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3459
3460         loadmodel->brushq3.data_effects = out;
3461         loadmodel->brushq3.num_effects = count;
3462
3463         for (i = 0;i < count;i++, in++, out++)
3464         {
3465                 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3466                 n = LittleLong(in->brushindex);
3467                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3468                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3469                 out->brush = loadmodel->brushq3.data_brushes + n;
3470                 out->unknown = LittleLong(in->unknown);
3471         }
3472 }
3473
3474 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3475 {
3476         q3dvertex_t *in;
3477         int i, count;
3478
3479         in = (void *)(mod_base + l->fileofs);
3480         if (l->filelen % sizeof(*in))
3481                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3482         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3483         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3484         loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3485         loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3486         loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3487         loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3488         loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3489         loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3490
3491         for (i = 0;i < count;i++, in++)
3492         {
3493                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3494                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3495                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3496                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3497                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3498                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3499                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3500                 // svector/tvector are calculated later in face loading
3501                 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3502                 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3503                 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3504                 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3505                 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3506                 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3507                 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3508                 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3509                 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3510                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3511                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3512                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3513                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3514         }
3515 }
3516
3517 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3518 {
3519         int *in;
3520         int *out;
3521         int i, count;
3522
3523         in = (void *)(mod_base + l->fileofs);
3524         if (l->filelen % sizeof(int[3]))
3525                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3526         count = l->filelen / sizeof(*in);
3527         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3528
3529         loadmodel->brushq3.num_triangles = count / 3;
3530         loadmodel->brushq3.data_element3i = out;
3531         loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3532
3533         for (i = 0;i < count;i++, in++, out++)
3534         {
3535                 *out = LittleLong(*in);
3536                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3537                 {
3538                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3539                         *out = 0;
3540                 }
3541         }
3542 }
3543
3544 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3545 {
3546         q3dlightmap_t *in;
3547         rtexture_t **out;
3548         int i, count;
3549
3550         in = (void *)(mod_base + l->fileofs);
3551         if (l->filelen % sizeof(*in))
3552                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3553         count = l->filelen / sizeof(*in);
3554         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3555
3556         loadmodel->brushq3.data_lightmaps = out;
3557         loadmodel->brushq3.num_lightmaps = count;
3558
3559         for (i = 0;i < count;i++, in++, out++)
3560                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3561 }
3562
3563 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3564 {
3565         q3dface_t *in;
3566         q3mface_t *out;
3567         int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3568         //int *originalelement3i;
3569         //int *originalneighbor3i;
3570         float *originalvertex3f;
3571         //float *originalsvector3f;
3572         //float *originaltvector3f;
3573         //float *originalnormal3f;
3574         float *originalcolor4f;
3575         float *originaltexcoordtexture2f;
3576         float *originaltexcoordlightmap2f;
3577         float *v;
3578
3579         in = (void *)(mod_base + l->fileofs);
3580         if (l->filelen % sizeof(*in))
3581                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3582         count = l->filelen / sizeof(*in);
3583         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3584
3585         loadmodel->brushq3.data_faces = out;
3586         loadmodel->brushq3.num_faces = count;
3587
3588         for (i = 0;i < count;i++, in++, out++)
3589         {
3590                 // check face type first
3591                 out->type = LittleLong(in->type);
3592                 if (out->type != Q3FACETYPE_POLYGON
3593                  && out->type != Q3FACETYPE_PATCH
3594                  && out->type != Q3FACETYPE_MESH
3595                  && out->type != Q3FACETYPE_FLARE)
3596                 {
3597                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3598                         out->type = 0; // error
3599                         continue;
3600                 }
3601
3602                 n = LittleLong(in->textureindex);
3603                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3604                 {
3605                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3606                         out->type = 0; // error
3607                         continue;
3608                         n = 0;
3609                 }
3610                 out->texture = loadmodel->brushq3.data_textures + n;
3611                 n = LittleLong(in->effectindex);
3612                 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3613                 {
3614                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3615                         n = -1;
3616                 }
3617                 if (n == -1)
3618                         out->effect = NULL;
3619                 else
3620                         out->effect = loadmodel->brushq3.data_effects + n;
3621                 n = LittleLong(in->lightmapindex);
3622                 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3623                 {
3624                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3625                         n = -1;
3626                 }
3627                 if (n == -1)
3628                         out->lightmaptexture = NULL;
3629                 else
3630                         out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3631
3632                 out->firstvertex = LittleLong(in->firstvertex);
3633                 out->numvertices = LittleLong(in->numvertices);
3634                 out->firstelement = LittleLong(in->firstelement);
3635                 out->numelements = LittleLong(in->numelements);
3636                 out->numtriangles = out->numelements / 3;
3637                 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3638                 {
3639                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->numvertices, loadmodel->brushq3.num_vertices);
3640                         out->type = 0; // error
3641                         continue;
3642                 }
3643                 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3644                 {
3645                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->numelements, loadmodel->brushq3.num_triangles * 3);
3646                         out->type = 0; // error
3647                         continue;
3648                 }
3649                 if (out->numtriangles * 3 != out->numelements)
3650                 {
3651                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3652                         out->type = 0; // error
3653                         continue;
3654                 }
3655                 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3656                 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3657                 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3658                 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3659                 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3660                 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3661                 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3662                 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3663                 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3664                 switch(out->type)
3665                 {
3666                 case Q3FACETYPE_POLYGON:
3667                 case Q3FACETYPE_MESH:
3668                         // no processing necessary
3669                         break;
3670                 case Q3FACETYPE_PATCH:
3671                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3672                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3673                         if (patchsize[0] < 1 || patchsize[1] < 1)
3674                         {
3675                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3676                                 out->type = 0; // error
3677                                 continue;
3678                         }
3679                         // convert patch to Q3FACETYPE_MESH
3680                         xlevel = mod_q3bsp_curves_subdivide_level.integer;
3681                         ylevel = mod_q3bsp_curves_subdivide_level.integer;
3682                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3683                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3684                         finalvertices = finalwidth * finalheight;
3685                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3686                         originalvertex3f = out->data_vertex3f;
3687                         //originalsvector3f = out->data_svector3f;
3688                         //originaltvector3f = out->data_tvector3f;
3689                         //originalnormal3f = out->data_normal3f;
3690                         originalcolor4f = out->data_color4f;
3691                         originaltexcoordtexture2f = out->data_texcoordtexture2f;
3692                         originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3693                         //originalelement3i = out->data_element3i;
3694                         //originalneighbor3i = out->data_neighbor3i;
3695                         out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
3696                         out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3697                         out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3698                         out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3699                         out->data_color4f = out->data_normal3f + finalvertices * 3;
3700                         out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3701                         out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3702                         out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
3703                         out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3704                         out->type = Q3FACETYPE_MESH;
3705                         out->firstvertex = -1;
3706                         out->numvertices = finalvertices;
3707                         out->firstelement = -1;
3708                         out->numtriangles = finaltriangles;
3709                         out->numelements = finaltriangles * 3;
3710                         // generate geometry
3711                         // (note: normals are skipped because they get recalculated)
3712                         QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3713                         QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3714                         QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3715                         QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3716                         // generate elements
3717                         e = out->data_element3i;
3718                         for (y = 0;y < finalheight - 1;y++)
3719                         {
3720                                 row0 = (y + 0) * finalwidth;
3721                                 row1 = (y + 1) * finalwidth;
3722                                 for (x = 0;x < finalwidth - 1;x++)
3723                                 {
3724                                         *e++ = row0;
3725                                         *e++ = row1;
3726                                         *e++ = row0 + 1;
3727                                         *e++ = row1;
3728                                         *e++ = row1 + 1;
3729                                         *e++ = row0 + 1;
3730                                         row0++;
3731                                         row1++;
3732                                 }
3733                         }
3734                         out->numtriangles = Mod_RemoveDegenerateTriangles(out->numtriangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3735                         if (developer.integer)
3736                         {
3737                                 if (out->numtriangles < finaltriangles)
3738                                         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->numvertices, finaltriangles, finaltriangles - out->numtriangles, out->numtriangles);
3739                                 else
3740                                         Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->numvertices, out->numtriangles);
3741                         }
3742                         // q3map does not put in collision brushes for curves... ugh
3743                         out->collisions = true;
3744                         break;
3745                 case Q3FACETYPE_FLARE:
3746                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3747                         // don't render it
3748                         out->numtriangles = 0;
3749                         break;
3750                 }
3751                 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3752                         if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3753                                 invalidelements++;
3754                 if (invalidelements)
3755                 {
3756                         Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->nativecontents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->nativecontents, out->firstvertex, out->numvertices, out->firstelement, out->numelements);
3757                         for (j = 0;j < out->numelements;j++)
3758                         {
3759                                 Con_Printf(" %i", out->data_element3i[j]);
3760                                 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3761                                         out->data_element3i[j] = 0;
3762                         }
3763                         Con_Printf("\n");
3764                 }
3765                 // for shadow volumes
3766                 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->numtriangles);
3767                 // for per pixel lighting
3768                 Mod_BuildTextureVectorsAndNormals(out->numvertices, out->numtriangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
3769                 // calculate a bounding box
3770                 VectorClear(out->mins);
3771                 VectorClear(out->maxs);
3772                 if (out->numvertices)
3773                 {
3774                         VectorCopy(out->data_vertex3f, out->mins);
3775                         VectorCopy(out->data_vertex3f, out->maxs);
3776                         for (j = 1, v = out->data_vertex3f + 3;j < out->numvertices;j++, v += 3)
3777                         {
3778                                 out->mins[0] = min(out->mins[0], v[0]);
3779                                 out->maxs[0] = max(out->maxs[0], v[0]);
3780                                 out->mins[1] = min(out->mins[1], v[1]);
3781                                 out->maxs[1] = max(out->maxs[1], v[1]);
3782                                 out->mins[2] = min(out->mins[2], v[2]);
3783                                 out->maxs[2] = max(out->maxs[2], v[2]);
3784                         }
3785                         out->mins[0] -= 1.0f;
3786                         out->mins[1] -= 1.0f;
3787                         out->mins[2] -= 1.0f;
3788                         out->maxs[0] += 1.0f;
3789                         out->maxs[1] += 1.0f;
3790                         out->maxs[2] += 1.0f;
3791                 }
3792         }
3793 }
3794
3795 static void Mod_Q3BSP_LoadModels(lump_t *l)
3796 {
3797         q3dmodel_t *in;
3798         q3mmodel_t *out;
3799         int i, j, n, c, count;
3800
3801         in = (void *)(mod_base + l->fileofs);
3802         if (l->filelen % sizeof(*in))
3803                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3804         count = l->filelen / sizeof(*in);
3805         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3806
3807         loadmodel->brushq3.data_models = out;
3808         loadmodel->brushq3.num_models = count;
3809
3810         for (i = 0;i < count;i++, in++, out++)
3811         {
3812                 for (j = 0;j < 3;j++)
3813                 {
3814                         out->mins[j] = LittleFloat(in->mins[j]);
3815                         out->maxs[j] = LittleFloat(in->maxs[j]);
3816                 }
3817                 n = LittleLong(in->firstface);
3818                 c = LittleLong(in->numfaces);
3819                 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3820                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3821                 out->firstface = loadmodel->brushq3.data_faces + n;
3822                 out->numfaces = c;
3823                 n = LittleLong(in->firstbrush);
3824                 c = LittleLong(in->numbrushes);
3825                 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3826                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3827                 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3828                 out->numbrushes = c;
3829         }
3830 }
3831
3832 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3833 {
3834         int *in;
3835         q3mbrush_t **out;
3836         int i, n, count;
3837
3838         in = (void *)(mod_base + l->fileofs);
3839         if (l->filelen % sizeof(*in))
3840                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3841         count = l->filelen / sizeof(*in);
3842         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3843
3844         loadmodel->brushq3.data_leafbrushes = out;
3845         loadmodel->brushq3.num_leafbrushes = count;
3846
3847         for (i = 0;i < count;i++, in++, out++)
3848         {
3849                 n = LittleLong(*in);
3850                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3851                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3852                 *out = loadmodel->brushq3.data_brushes + n;
3853         }
3854 }
3855
3856 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3857 {
3858         int *in;
3859         q3mface_t **out;
3860         int i, n, count;
3861
3862         in = (void *)(mod_base + l->fileofs);
3863         if (l->filelen % sizeof(*in))
3864                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3865         count = l->filelen / sizeof(*in);
3866         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3867
3868         loadmodel->brushq3.data_leaffaces = out;
3869         loadmodel->brushq3.num_leaffaces = count;
3870
3871         for (i = 0;i < count;i++, in++, out++)
3872         {
3873                 n = LittleLong(*in);
3874                 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3875                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3876                 *out = loadmodel->brushq3.data_faces + n;
3877         }
3878 }
3879
3880 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3881 {
3882         q3dleaf_t *in;
3883         q3mleaf_t *out;
3884         int i, j, n, c, count;
3885
3886         in = (void *)(mod_base + l->fileofs);
3887         if (l->filelen % sizeof(*in))
3888                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3889         count = l->filelen / sizeof(*in);
3890         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3891
3892         loadmodel->brushq3.data_leafs = out;
3893         loadmodel->brushq3.num_leafs = count;
3894
3895         for (i = 0;i < count;i++, in++, out++)
3896         {
3897                 out->isnode = false;
3898                 out->parent = NULL;
3899                 out->clusterindex = LittleLong(in->clusterindex);
3900                 out->areaindex = LittleLong(in->areaindex);
3901                 for (j = 0;j < 3;j++)
3902                 {
3903                         // yes the mins/maxs are ints
3904                         out->mins[j] = LittleLong(in->mins[j]);
3905                         out->maxs[j] = LittleLong(in->maxs[j]);
3906                 }
3907                 n = LittleLong(in->firstleafface);
3908                 c = LittleLong(in->numleaffaces);
3909                 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3910                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3911                 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3912                 out->numleaffaces = c;
3913                 n = LittleLong(in->firstleafbrush);
3914                 c = LittleLong(in->numleafbrushes);
3915                 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3916                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3917                 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3918                 out->numleafbrushes = c;
3919         }
3920 }
3921
3922 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3923 {
3924         if (node->parent)
3925                 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3926         node->parent = parent;
3927         if (node->isnode)
3928         {
3929                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3930                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3931         }
3932 }
3933
3934 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3935 {
3936         q3dnode_t *in;
3937         q3mnode_t *out;
3938         int i, j, n, count;
3939
3940         in = (void *)(mod_base + l->fileofs);
3941         if (l->filelen % sizeof(*in))
3942                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3943         count = l->filelen / sizeof(*in);
3944         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3945
3946         loadmodel->brushq3.data_nodes = out;
3947         loadmodel->brushq3.num_nodes = count;
3948
3949         for (i = 0;i < count;i++, in++, out++)
3950         {
3951                 out->isnode = true;
3952                 out->parent = NULL;
3953                 n = LittleLong(in->planeindex);
3954                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3955                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3956                 out->plane = loadmodel->brushq3.data_planes + n;
3957                 for (j = 0;j < 2;j++)
3958                 {
3959                         n = LittleLong(in->childrenindex[j]);
3960                         if (n >= 0)
3961                         {
3962                                 if (n >= loadmodel->brushq3.num_nodes)
3963                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3964                                 out->children[j] = loadmodel->brushq3.data_nodes + n;
3965                         }
3966                         else
3967                         {
3968                                 n = -1 - n;
3969                                 if (n >= loadmodel->brushq3.num_leafs)
3970                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3971                                 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3972                         }
3973                 }
3974                 for (j = 0;j < 3;j++)
3975                 {
3976                         // yes the mins/maxs are ints
3977                         out->mins[j] = LittleLong(in->mins[j]);
3978                         out->maxs[j] = LittleLong(in->maxs[j]);
3979                 }
3980         }
3981
3982         // set the parent pointers
3983         Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3984 }
3985
3986 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3987 {
3988         q3dlightgrid_t *in;
3989         q3dlightgrid_t *out;
3990         int count;
3991
3992         in = (void *)(mod_base + l->fileofs);
3993         if (l->filelen % sizeof(*in))
3994                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
3995         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
3996         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
3997         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
3998         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3999         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4000         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4001         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4002         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4003         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4004         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4005         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4006         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4007         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4008         if (l->filelen < count * (int)sizeof(*in))
4009                 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]);
4010         if (l->filelen != count * (int)sizeof(*in))
4011                 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4012
4013         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4014         loadmodel->brushq3.data_lightgrid = out;
4015         loadmodel->brushq3.num_lightgrid = count;
4016
4017         // no swapping or validation necessary
4018         memcpy(out, in, count * (int)sizeof(*out));
4019
4020         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]);
4021         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]);
4022 }
4023
4024 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4025 {
4026         q3dpvs_t *in;
4027         int totalchains;
4028
4029         if (l->filelen == 0)
4030                 return;
4031
4032         in = (void *)(mod_base + l->fileofs);
4033         if (l->filelen < 9)
4034                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4035
4036         loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4037         loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4038         if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4039                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4040         totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4041         if (l->filelen < totalchains + (int)sizeof(*in))
4042                 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);
4043
4044         loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4045         memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4046 }
4047
4048 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4049 {
4050         // FIXME: finish this code
4051         VectorCopy(in, out);
4052 }
4053
4054 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4055 {
4056         int i, j, k, index[3];
4057         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4058         q3dlightgrid_t *a, *s;
4059         // FIXME: write this
4060         if (!model->brushq3.num_lightgrid)
4061         {
4062                 ambientcolor[0] += 128;
4063                 ambientcolor[1] += 128;
4064                 ambientcolor[2] += 128;
4065                 return;
4066         }
4067         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4068         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4069         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4070         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0]);
4071         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1]);
4072         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2]);
4073         index[0] = (int)floor(transformed[0]);
4074         index[1] = (int)floor(transformed[1]);
4075         index[2] = (int)floor(transformed[2]);
4076         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4077         // now lerp the values
4078         VectorClear(diffusenormal);
4079         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4080         for (k = 0;k < 2;k++)
4081         {
4082                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4083                 for (j = 0;j < 2;j++)
4084                 {
4085                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4086                         for (i = 0;i < 2;i++)
4087                         {
4088                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4089                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4090                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4091                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4092                                 pitch = s->diffusepitch * M_PI / 128;
4093                                 yaw = s->diffuseyaw * M_PI / 128;
4094                                 sinpitch = sin(pitch);
4095                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4096                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4097                                 diffusenormal[2] += blend * (cos(pitch));
4098                                 //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)));
4099                         }
4100                 }
4101         }
4102         VectorNormalize(diffusenormal);
4103         //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]);
4104 }
4105
4106 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)
4107 {
4108         int i, startside, endside;
4109         float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4110         q3mleaf_t *leaf;
4111         q3mface_t *face;
4112         colbrushf_t *brush;
4113         if (startfrac >= trace->fraction)
4114                 return;
4115         // note: all line fragments past first impact fraction are ignored
4116         while (node->isnode)
4117         {
4118                 // recurse down node sides
4119                 dist1 = PlaneDiff(start, node->plane);
4120                 dist2 = PlaneDiff(end, node->plane);
4121                 startside = dist1 < 0;
4122                 endside = dist2 < 0;
4123                 if (startside == endside)
4124                 {
4125                         // most of the time the line fragment is on one side of the plane
4126                         node = node->children[startside];
4127                 }
4128                 else
4129                 {
4130                         // line crosses node plane, split the line
4131                         midfrac = dist1 / (dist1 - dist2);
4132                         VectorLerp(linestart, midfrac, lineend, mid);
4133                         // take the near side first
4134                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4135                         if (midfrac < trace->fraction)
4136                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4137                         return;
4138                 }
4139         }
4140         // hit a leaf
4141         segmentmins[0] = min(start[0], end[0]);
4142         segmentmins[1] = min(start[1], end[1]);
4143         segmentmins[2] = min(start[2], end[2]);
4144         segmentmaxs[0] = max(start[0], end[0]);
4145         segmentmaxs[1] = max(start[1], end[1]);
4146         segmentmaxs[2] = max(start[2], end[2]);
4147         leaf = (q3mleaf_t *)node;
4148         for (i = 0;i < leaf->numleafbrushes;i++)
4149         {
4150                 if (startfrac >= trace->fraction)
4151                         return;
4152                 brush = leaf->firstleafbrush[i]->colbrushf;
4153                 if (brush && brush->markframe != markframe)
4154                 {
4155                         brush->markframe = markframe;
4156                         if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4157                                 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4158                 }
4159         }
4160         if (mod_q3bsp_curves_collisions.integer)
4161         {
4162                 for (i = 0;i < leaf->numleaffaces;i++)
4163                 {
4164                         if (startfrac >= trace->fraction)
4165                                 return;
4166                         face = leaf->firstleafface[i];
4167                         if (face->collisions && face->collisionmarkframe != markframe)
4168                         {
4169                                 face->collisionmarkframe = markframe;
4170                                 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4171                                         Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4172                         }
4173                 }
4174         }
4175 }
4176
4177 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)
4178 {
4179         int i, sides;
4180         float nodesegmentmins[3], nodesegmentmaxs[3];
4181         q3mleaf_t *leaf;
4182         colbrushf_t *brush;
4183         q3mface_t *face;
4184         nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4185         nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4186         nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4187         nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4188         nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4189         nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4190         if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4191                 return;
4192         if (node->isnode)
4193         {
4194                 // recurse down node sides
4195                 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4196                 if (sides == 3)
4197                 {
4198                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4199                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4200                 }
4201                 else if (sides == 2)
4202                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4203                 else // sides == 1
4204                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4205                 /*
4206                 dist = node->plane->dist - (1.0f / 8.0f);
4207                 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4208                 {
4209                         if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4210                         {
4211                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4212                                 break;
4213                         }
4214                 }
4215                 */
4216                 /*
4217                 dist = node->plane->dist + (1.0f / 8.0f);
4218                 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4219                 {
4220                         if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4221                         {
4222                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4223                                 break;
4224                         }
4225                 }
4226                 */
4227                 /*
4228                 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4229                 if (sides & 1)
4230                         Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4231                 if (sides & 2)
4232                         Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4233                 */
4234         }
4235         else
4236         {
4237                 // hit a leaf
4238                 leaf = (q3mleaf_t *)node;
4239                 for (i = 0;i < leaf->numleafbrushes;i++)
4240                 {
4241                         brush = leaf->firstleafbrush[i]->colbrushf;
4242                         if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4243                         {
4244                                 brush->markframe = markframe;
4245                                 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4246                         }
4247                 }
4248                 if (mod_q3bsp_curves_collisions.integer)
4249                 {
4250                         for (i = 0;i < leaf->numleaffaces;i++)
4251                         {
4252                                 face = leaf->firstleafface[i];
4253                                 // 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
4254                                 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4255                                         Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4256                         }
4257                 }
4258         }
4259 }
4260
4261 static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask)
4262 {
4263         int i;
4264         float segmentmins[3], segmentmaxs[3];
4265         colbrushf_t *thisbrush_start, *thisbrush_end;
4266         matrix4x4_t startmatrix, endmatrix;
4267         static int markframe = 0;
4268         q3mface_t *face;
4269         memset(trace, 0, sizeof(*trace));
4270         trace->fraction = 1;
4271         trace->hitsupercontentsmask = hitsupercontentsmask;
4272         Matrix4x4_CreateIdentity(&startmatrix);
4273         Matrix4x4_CreateIdentity(&endmatrix);
4274         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4275         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4276         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4277         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4278         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4279         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4280         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4281         {
4282                 // line trace
4283                 if (model->brushq3.submodel)
4284                 {
4285                         for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4286                                 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4287                                         Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4288                         if (mod_q3bsp_curves_collisions.integer)
4289                         {
4290                                 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4291                                 {
4292                                         face = model->brushq3.data_thismodel->firstface + i;
4293                                         if (face->collisions)
4294                                                 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4295                                 }
4296                         }
4297                 }
4298                 else
4299                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4300         }
4301         else
4302         {
4303                 // box trace, performed as brush trace
4304                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4305                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4306                 if (model->brushq3.submodel)
4307                 {
4308                         for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4309                                 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4310                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4311                         if (mod_q3bsp_curves_collisions.integer)
4312                         {
4313                                 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4314                                 {
4315                                         face = model->brushq3.data_thismodel->firstface + i;
4316                                         if (face->collisions)
4317                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4318                                 }
4319                         }
4320                 }
4321                 else
4322                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4323         }
4324 }
4325
4326
4327 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)
4328 {
4329         int clusterindex;
4330 loc0:
4331         if (!node->isnode)
4332         {
4333                 // leaf
4334                 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4335                 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4336         }
4337
4338         // node - recurse down the BSP tree
4339         switch (BoxOnPlaneSide(mins, maxs, node->plane))
4340         {
4341         case 1: // front
4342                 node = node->children[0];
4343                 goto loc0;
4344         case 2: // back
4345                 node = node->children[1];
4346                 goto loc0;
4347         default: // crossing
4348                 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4349                         return true;
4350                 node = node->children[1];
4351                 goto loc0;
4352         }
4353         // never reached
4354         return false;
4355 }
4356
4357 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4358 {
4359         return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4360 }
4361
4362 //Returns PVS data for a given point
4363 //(note: can return NULL)
4364 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4365 {
4366         q3mnode_t *node;
4367         Mod_CheckLoaded(model);
4368         node = model->brushq3.data_nodes;
4369         while (node->isnode)
4370                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4371         if (((q3mleaf_t *)node)->clusterindex >= 0)
4372                 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4373         else
4374                 return NULL;
4375 }
4376
4377 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4378 {
4379         int i;
4380         float d;
4381         qbyte *pvs;
4382
4383         while (node->isnode)
4384         {
4385                 d = PlaneDiff(org, node->plane);
4386                 if (d > radius)
4387                         node = node->children[0];
4388                 else if (d < -radius)
4389                         node = node->children[1];
4390                 else
4391                 {
4392                         // go down both sides
4393                         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4394                         node = node->children[1];
4395                 }
4396         }
4397         // if this is a leaf with a pvs, accumulate the pvs bits
4398         if (((q3mleaf_t *)node)->clusterindex >= 0)
4399         {
4400                 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4401                 for (i = 0;i < pvsbytes;i++)
4402                         pvsbuffer[i] |= pvs[i];
4403         }
4404         return;
4405 }
4406
4407 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4408 //of the given point.
4409 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4410 {
4411         int bytes = model->brushq3.num_pvschainlength;
4412         bytes = min(bytes, pvsbufferlength);
4413         memset(pvsbuffer, 0, bytes);
4414         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4415         return bytes;
4416 }
4417
4418
4419 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4420 {
4421         int supercontents = 0;
4422         if (nativecontents & Q2CONTENTS_SOLID)
4423                 supercontents |= SUPERCONTENTS_SOLID;
4424         if (nativecontents & Q2CONTENTS_WATER)
4425                 supercontents |= SUPERCONTENTS_WATER;
4426         if (nativecontents & Q2CONTENTS_SLIME)
4427                 supercontents |= SUPERCONTENTS_SLIME;
4428         if (nativecontents & Q2CONTENTS_LAVA)
4429                 supercontents |= SUPERCONTENTS_LAVA;
4430         return supercontents;
4431 }
4432
4433 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4434 {
4435         int nativecontents = 0;
4436         if (supercontents & SUPERCONTENTS_SOLID)
4437                 nativecontents |= Q2CONTENTS_SOLID;
4438         if (supercontents & SUPERCONTENTS_WATER)
4439                 nativecontents |= Q2CONTENTS_WATER;
4440         if (supercontents & SUPERCONTENTS_SLIME)
4441                 nativecontents |= Q2CONTENTS_SLIME;
4442         if (supercontents & SUPERCONTENTS_LAVA)
4443                 nativecontents |= Q2CONTENTS_LAVA;
4444         return nativecontents;
4445 }
4446
4447 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4448 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4449 //extern void R_Q3BSP_DrawFakeShadow(struct entity_render_s *ent);
4450 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4451 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);
4452 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4453 {
4454         int i;
4455         q3dheader_t *header;
4456         float corner[3], yawradius, modelradius;
4457
4458         mod->type = mod_brushq3;
4459         mod->numframes = 1;
4460         mod->numskins = 1;
4461
4462         header = (q3dheader_t *)buffer;
4463
4464         i = LittleLong(header->version);
4465         if (i != Q3BSPVERSION)
4466                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4467         if (loadmodel->isworldmodel)
4468         {
4469                 Cvar_SetValue("halflifebsp", false);
4470                 // until we get a texture for it...
4471                 R_ResetQuakeSky();
4472         }
4473
4474         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4475         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4476         mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4477         mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4478         mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4479         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4480         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4481         mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4482         //mod->DrawSky = R_Q3BSP_DrawSky;
4483         mod->Draw = R_Q3BSP_Draw;
4484         //mod->DrawFakeShadow = R_Q3BSP_DrawFakeShadow;
4485         mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4486         mod->DrawLight = R_Q3BSP_DrawLight;
4487
4488         mod_base = (qbyte *)header;
4489
4490         // swap all the lumps
4491         for (i = 0;i < (int) sizeof(*header) / 4;i++)
4492                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4493
4494         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4495         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4496         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4497         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4498         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4499         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4500         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4501         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4502         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4503         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4504         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4505         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4506         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4507         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4508         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4509         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4510         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4511         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4512
4513         for (i = 0;i < loadmodel->brushq3.num_models;i++)
4514         {
4515                 if (i == 0)
4516                         mod = loadmodel;
4517                 else
4518                 {
4519                         char name[10];
4520                         // LordHavoc: only register submodels if it is the world
4521                         // (prevents bsp models from replacing world submodels)
4522                         if (!loadmodel->isworldmodel)
4523                                 continue;
4524                         // duplicate the basic information
4525                         sprintf(name, "*%i", i);
4526                         mod = Mod_FindName(name);
4527                         *mod = *loadmodel;
4528                         strcpy(mod->name, name);
4529                         // textures and memory belong to the main model
4530                         mod->texturepool = NULL;
4531                         mod->mempool = NULL;
4532                 }
4533                 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4534                 mod->brushq3.submodel = i;
4535
4536                 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4537                 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4538                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4539                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4540                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4541                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4542                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4543                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4544                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4545                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4546                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4547                 mod->yawmins[2] = mod->normalmins[2];
4548                 mod->yawmaxs[2] = mod->normalmaxs[2];
4549                 mod->radius = modelradius;
4550                 mod->radius2 = modelradius * modelradius;
4551         }
4552 }
4553
4554 void Mod_IBSP_Load(model_t *mod, void *buffer)
4555 {
4556         int i = LittleLong(((int *)buffer)[1]);
4557         if (i == Q3BSPVERSION)
4558                 Mod_Q3BSP_Load(mod,buffer);
4559         else if (i == Q2BSPVERSION)
4560                 Mod_Q2BSP_Load(mod,buffer);
4561         else
4562                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4563 }
4564
4565 void Mod_MAP_Load(model_t *mod, void *buffer)
4566 {
4567         Host_Error("Mod_MAP_Load: not yet implemented\n");
4568 }
4569