]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
the new partial-update entity compression protocol now works for the first time ever
[divverent/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24 #include "winding.h"
25 #include "curves.h"
26
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
28
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
30
31 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
32 cvar_t halflifebsp = {0, "halflifebsp", "0"};
33 cvar_t r_novis = {0, "r_novis", "0"};
34 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
35 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
36 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
37 cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"};
38 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
39 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
40
41 void Mod_BrushInit(void)
42 {
43 //      Cvar_RegisterVariable(&r_subdivide_size);
44         Cvar_RegisterVariable(&halflifebsp);
45         Cvar_RegisterVariable(&r_novis);
46         Cvar_RegisterVariable(&r_miplightmaps);
47         Cvar_RegisterVariable(&r_lightmaprgba);
48         Cvar_RegisterVariable(&r_nosurftextures);
49         Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level);
50         Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
51         Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
52         memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
53 }
54
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
56 {
57         mnode_t *node;
58
59         if (model == NULL)
60                 return NULL;
61
62         Mod_CheckLoaded(model);
63
64         // LordHavoc: modified to start at first clip node,
65         // in other words: first node of the (sub)model
66         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
67         while (node->contents == 0)
68                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
69
70         return (mleaf_t *)node;
71 }
72
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
74 {
75         int i;
76         mleaf_t *leaf;
77         leaf = Mod_Q1BSP_PointInLeaf(model, p);
78         if (leaf)
79         {
80                 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
81                 if (i)
82                 {
83                         memcpy(out, leaf->ambient_sound_level, i);
84                         out += i;
85                         outsize -= i;
86                 }
87         }
88         if (outsize)
89                 memset(out, 0, outsize);
90 }
91
92
93 static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
94 {
95         int leafnum;
96 loc0:
97         if (node->contents < 0)
98         {
99                 // leaf
100                 if (node->contents == CONTENTS_SOLID)
101                         return false;
102                 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103                 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
104         }
105
106         // node - recurse down the BSP tree
107         switch (BoxOnPlaneSide(mins, maxs, node->plane))
108         {
109         case 1: // front
110                 node = node->children[0];
111                 goto loc0;
112         case 2: // back
113                 node = node->children[1];
114                 goto loc0;
115         default: // crossing
116                 if (node->children[0]->contents != CONTENTS_SOLID)
117                         if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
118                                 return true;
119                 node = node->children[1];
120                 goto loc0;
121         }
122         // never reached
123         return false;
124 }
125
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
127 {
128         return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
129 }
130
131 /*
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
133 {
134         mnode_t *node;
135
136         if (model == NULL)
137                 return CONTENTS_EMPTY;
138
139         Mod_CheckLoaded(model);
140
141         // LordHavoc: modified to start at first clip node,
142         // in other words: first node of the (sub)model
143         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
144         while (node->contents == 0)
145                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
146
147         return ((mleaf_t *)node)->contents;
148 }
149 */
150
151 typedef struct findnonsolidlocationinfo_s
152 {
153         vec3_t center;
154         vec_t radius;
155         vec3_t nudge;
156         vec_t bestdist;
157         model_t *model;
158 }
159 findnonsolidlocationinfo_t;
160
161 #if 0
162 extern cvar_t samelevel;
163 #endif
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
165 {
166         int i, surfnum, k, *tri, *mark;
167         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
168 #if 0
169         float surfnormal[3];
170 #endif
171         msurface_t *surf;
172         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
1837                 out->pvsdata = pvs;
1838                 pvs += pvschainbytes;
1839
1840                 p = LittleLong(in->visofs);
1841                 if (p >= 0)
1842                         Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1843                 else
1844                         memset(out->pvsdata, 0xFF, pvschainbytes);
1845
1846                 for (j = 0;j < 4;j++)
1847                         out->ambient_sound_level[j] = in->ambient_level[j];
1848
1849                 // FIXME: Insert caustics here
1850         }
1851 }
1852
1853 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1854 {
1855         dclipnode_t *in, *out;
1856         int                     i, count;
1857         hull_t          *hull;
1858
1859         in = (void *)(mod_base + l->fileofs);
1860         if (l->filelen % sizeof(*in))
1861                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1862         count = l->filelen / sizeof(*in);
1863         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1864
1865         loadmodel->brushq1.clipnodes = out;
1866         loadmodel->brushq1.numclipnodes = count;
1867
1868         if (loadmodel->brush.ishlbsp)
1869         {
1870                 hull = &loadmodel->brushq1.hulls[1];
1871                 hull->clipnodes = out;
1872                 hull->firstclipnode = 0;
1873                 hull->lastclipnode = count-1;
1874                 hull->planes = loadmodel->brushq1.planes;
1875                 hull->clip_mins[0] = -16;
1876                 hull->clip_mins[1] = -16;
1877                 hull->clip_mins[2] = -36;
1878                 hull->clip_maxs[0] = 16;
1879                 hull->clip_maxs[1] = 16;
1880                 hull->clip_maxs[2] = 36;
1881                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1882
1883                 hull = &loadmodel->brushq1.hulls[2];
1884                 hull->clipnodes = out;
1885                 hull->firstclipnode = 0;
1886                 hull->lastclipnode = count-1;
1887                 hull->planes = loadmodel->brushq1.planes;
1888                 hull->clip_mins[0] = -32;
1889                 hull->clip_mins[1] = -32;
1890                 hull->clip_mins[2] = -32;
1891                 hull->clip_maxs[0] = 32;
1892                 hull->clip_maxs[1] = 32;
1893                 hull->clip_maxs[2] = 32;
1894                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1895
1896                 hull = &loadmodel->brushq1.hulls[3];
1897                 hull->clipnodes = out;
1898                 hull->firstclipnode = 0;
1899                 hull->lastclipnode = count-1;
1900                 hull->planes = loadmodel->brushq1.planes;
1901                 hull->clip_mins[0] = -16;
1902                 hull->clip_mins[1] = -16;
1903                 hull->clip_mins[2] = -18;
1904                 hull->clip_maxs[0] = 16;
1905                 hull->clip_maxs[1] = 16;
1906                 hull->clip_maxs[2] = 18;
1907                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1908         }
1909         else
1910         {
1911                 hull = &loadmodel->brushq1.hulls[1];
1912                 hull->clipnodes = out;
1913                 hull->firstclipnode = 0;
1914                 hull->lastclipnode = count-1;
1915                 hull->planes = loadmodel->brushq1.planes;
1916                 hull->clip_mins[0] = -16;
1917                 hull->clip_mins[1] = -16;
1918                 hull->clip_mins[2] = -24;
1919                 hull->clip_maxs[0] = 16;
1920                 hull->clip_maxs[1] = 16;
1921                 hull->clip_maxs[2] = 32;
1922                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1923
1924                 hull = &loadmodel->brushq1.hulls[2];
1925                 hull->clipnodes = out;
1926                 hull->firstclipnode = 0;
1927                 hull->lastclipnode = count-1;
1928                 hull->planes = loadmodel->brushq1.planes;
1929                 hull->clip_mins[0] = -32;
1930                 hull->clip_mins[1] = -32;
1931                 hull->clip_mins[2] = -24;
1932                 hull->clip_maxs[0] = 32;
1933                 hull->clip_maxs[1] = 32;
1934                 hull->clip_maxs[2] = 64;
1935                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1936         }
1937
1938         for (i=0 ; i<count ; i++, out++, in++)
1939         {
1940                 out->planenum = LittleLong(in->planenum);
1941                 out->children[0] = LittleShort(in->children[0]);
1942                 out->children[1] = LittleShort(in->children[1]);
1943                 if (out->children[0] >= count || out->children[1] >= count)
1944                         Host_Error("Corrupt clipping hull(out of range child)\n");
1945         }
1946 }
1947
1948 //Duplicate the drawing hull structure as a clipping hull
1949 static void Mod_Q1BSP_MakeHull0(void)
1950 {
1951         mnode_t         *in;
1952         dclipnode_t *out;
1953         int                     i;
1954         hull_t          *hull;
1955
1956         hull = &loadmodel->brushq1.hulls[0];
1957
1958         in = loadmodel->brushq1.nodes;
1959         out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1960
1961         hull->clipnodes = out;
1962         hull->firstclipnode = 0;
1963         hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1964         hull->planes = loadmodel->brushq1.planes;
1965
1966         for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1967         {
1968                 out->planenum = in->plane - loadmodel->brushq1.planes;
1969                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1970                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1971         }
1972 }
1973
1974 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1975 {
1976         int i, j;
1977         short *in;
1978
1979         in = (void *)(mod_base + l->fileofs);
1980         if (l->filelen % sizeof(*in))
1981                 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1982         loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1983         loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1984
1985         for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1986         {
1987                 j = (unsigned) LittleShort(in[i]);
1988                 if (j >= loadmodel->brushq1.numsurfaces)
1989                         Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
1990                 loadmodel->brushq1.marksurfaces[i] = j;
1991         }
1992 }
1993
1994 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
1995 {
1996         int             i;
1997         int             *in;
1998
1999         in = (void *)(mod_base + l->fileofs);
2000         if (l->filelen % sizeof(*in))
2001                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2002         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2003         loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2004
2005         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2006                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2007 }
2008
2009
2010 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2011 {
2012         int                     i;
2013         mplane_t        *out;
2014         dplane_t        *in;
2015
2016         in = (void *)(mod_base + l->fileofs);
2017         if (l->filelen % sizeof(*in))
2018                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2019
2020         loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2021         loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2022
2023         for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2024         {
2025                 out->normal[0] = LittleFloat(in->normal[0]);
2026                 out->normal[1] = LittleFloat(in->normal[1]);
2027                 out->normal[2] = LittleFloat(in->normal[2]);
2028                 out->dist = LittleFloat(in->dist);
2029
2030                 PlaneClassify(out);
2031         }
2032 }
2033
2034 typedef struct portal_s
2035 {
2036         mplane_t plane;
2037         mnode_t *nodes[2];              // [0] = front side of plane
2038         struct portal_s *next[2];
2039         winding_t *winding;
2040         struct portal_s *chain; // all portals are linked into a list
2041 }
2042 portal_t;
2043
2044 static portal_t *portalchain;
2045
2046 /*
2047 ===========
2048 AllocPortal
2049 ===========
2050 */
2051 static portal_t *AllocPortal(void)
2052 {
2053         portal_t *p;
2054         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2055         p->chain = portalchain;
2056         portalchain = p;
2057         return p;
2058 }
2059
2060 static void FreePortal(portal_t *p)
2061 {
2062         Mem_Free(p);
2063 }
2064
2065 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2066 {
2067         // calculate children first
2068         if (node->children[0]->contents >= 0)
2069                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2070         if (node->children[1]->contents >= 0)
2071                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2072
2073         // make combined bounding box from children
2074         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2075         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2076         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2077         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2078         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2079         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2080 }
2081
2082 static void Mod_Q1BSP_FinalizePortals(void)
2083 {
2084         int i, j, numportals, numpoints;
2085         portal_t *p, *pnext;
2086         mportal_t *portal;
2087         mvertex_t *point;
2088         mleaf_t *leaf, *endleaf;
2089         winding_t *w;
2090
2091         // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2092         leaf = loadmodel->brushq1.leafs;
2093         endleaf = leaf + loadmodel->brushq1.numleafs;
2094         for (;leaf < endleaf;leaf++)
2095         {
2096                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2097                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2098         }
2099         p = portalchain;
2100         while (p)
2101         {
2102                 if (p->winding)
2103                 {
2104                         for (i = 0;i < 2;i++)
2105                         {
2106                                 leaf = (mleaf_t *)p->nodes[i];
2107                                 w = p->winding;
2108                                 for (j = 0;j < w->numpoints;j++)
2109                                 {
2110                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2111                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2112                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2113                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2114                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2115                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2116                                 }
2117                         }
2118                 }
2119                 p = p->chain;
2120         }
2121
2122         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2123
2124         // tally up portal and point counts
2125         p = portalchain;
2126         numportals = 0;
2127         numpoints = 0;
2128         while (p)
2129         {
2130                 // note: this check must match the one below or it will usually corrupt memory
2131                 // 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
2132                 if (p->winding && p->nodes[0] != p->nodes[1]
2133                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2134                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2135                 {
2136                         numportals += 2;
2137                         numpoints += p->winding->numpoints * 2;
2138                 }
2139                 p = p->chain;
2140         }
2141         loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2142         loadmodel->brushq1.numportals = numportals;
2143         loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2144         loadmodel->brushq1.numportalpoints = numpoints;
2145         // clear all leaf portal chains
2146         for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2147                 loadmodel->brushq1.leafs[i].portals = NULL;
2148         // process all portals in the global portal chain, while freeing them
2149         portal = loadmodel->brushq1.portals;
2150         point = loadmodel->brushq1.portalpoints;
2151         p = portalchain;
2152         portalchain = NULL;
2153         while (p)
2154         {
2155                 pnext = p->chain;
2156
2157                 if (p->winding)
2158                 {
2159                         // note: this check must match the one above or it will usually corrupt memory
2160                         // 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
2161                         if (p->nodes[0] != p->nodes[1]
2162                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2163                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2164                         {
2165                                 // first make the back to front portal(forward portal)
2166                                 portal->points = point;
2167                                 portal->numpoints = p->winding->numpoints;
2168                                 portal->plane.dist = p->plane.dist;
2169                                 VectorCopy(p->plane.normal, portal->plane.normal);
2170                                 portal->here = (mleaf_t *)p->nodes[1];
2171                                 portal->past = (mleaf_t *)p->nodes[0];
2172                                 // copy points
2173                                 for (j = 0;j < portal->numpoints;j++)
2174                                 {
2175                                         VectorCopy(p->winding->points[j], point->position);
2176                                         point++;
2177                                 }
2178                                 PlaneClassify(&portal->plane);
2179
2180                                 // link into leaf's portal chain
2181                                 portal->next = portal->here->portals;
2182                                 portal->here->portals = portal;
2183
2184                                 // advance to next portal
2185                                 portal++;
2186
2187                                 // then make the front to back portal(backward portal)
2188                                 portal->points = point;
2189                                 portal->numpoints = p->winding->numpoints;
2190                                 portal->plane.dist = -p->plane.dist;
2191                                 VectorNegate(p->plane.normal, portal->plane.normal);
2192                                 portal->here = (mleaf_t *)p->nodes[0];
2193                                 portal->past = (mleaf_t *)p->nodes[1];
2194                                 // copy points
2195                                 for (j = portal->numpoints - 1;j >= 0;j--)
2196                                 {
2197                                         VectorCopy(p->winding->points[j], point->position);
2198                                         point++;
2199                                 }
2200                                 PlaneClassify(&portal->plane);
2201
2202                                 // link into leaf's portal chain
2203                                 portal->next = portal->here->portals;
2204                                 portal->here->portals = portal;
2205
2206                                 // advance to next portal
2207                                 portal++;
2208                         }
2209                         Winding_Free(p->winding);
2210                 }
2211                 FreePortal(p);
2212                 p = pnext;
2213         }
2214 }
2215
2216 /*
2217 =============
2218 AddPortalToNodes
2219 =============
2220 */
2221 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2222 {
2223         if (!front)
2224                 Host_Error("AddPortalToNodes: NULL front node");
2225         if (!back)
2226                 Host_Error("AddPortalToNodes: NULL back node");
2227         if (p->nodes[0] || p->nodes[1])
2228                 Host_Error("AddPortalToNodes: already included");
2229         // 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
2230
2231         p->nodes[0] = front;
2232         p->next[0] = (portal_t *)front->portals;
2233         front->portals = (mportal_t *)p;
2234
2235         p->nodes[1] = back;
2236         p->next[1] = (portal_t *)back->portals;
2237         back->portals = (mportal_t *)p;
2238 }
2239
2240 /*
2241 =============
2242 RemovePortalFromNode
2243 =============
2244 */
2245 static void RemovePortalFromNodes(portal_t *portal)
2246 {
2247         int i;
2248         mnode_t *node;
2249         void **portalpointer;
2250         portal_t *t;
2251         for (i = 0;i < 2;i++)
2252         {
2253                 node = portal->nodes[i];
2254
2255                 portalpointer = (void **) &node->portals;
2256                 while (1)
2257                 {
2258                         t = *portalpointer;
2259                         if (!t)
2260                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2261
2262                         if (t == portal)
2263                         {
2264                                 if (portal->nodes[0] == node)
2265                                 {
2266                                         *portalpointer = portal->next[0];
2267                                         portal->nodes[0] = NULL;
2268                                 }
2269                                 else if (portal->nodes[1] == node)
2270                                 {
2271                                         *portalpointer = portal->next[1];
2272                                         portal->nodes[1] = NULL;
2273                                 }
2274                                 else
2275                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2276                                 break;
2277                         }
2278
2279                         if (t->nodes[0] == node)
2280                                 portalpointer = (void **) &t->next[0];
2281                         else if (t->nodes[1] == node)
2282                                 portalpointer = (void **) &t->next[1];
2283                         else
2284                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2285                 }
2286         }
2287 }
2288
2289 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2290 {
2291         int side;
2292         mnode_t *front, *back, *other_node;
2293         mplane_t clipplane, *plane;
2294         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2295         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2296
2297         // if a leaf, we're done
2298         if (node->contents)
2299                 return;
2300
2301         plane = node->plane;
2302
2303         front = node->children[0];
2304         back = node->children[1];
2305         if (front == back)
2306                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2307
2308         // create the new portal by generating a polygon for the node plane,
2309         // and clipping it by all of the other portals(which came from nodes above this one)
2310         nodeportal = AllocPortal();
2311         nodeportal->plane = *node->plane;
2312
2313         nodeportalwinding = Winding_NewFromPlane(node->plane->normal[0], node->plane->normal[1], node->plane->normal[2], node->plane->dist);
2314         side = 0;       // shut up compiler warning
2315         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2316         {
2317                 clipplane = portal->plane;
2318                 if (portal->nodes[0] == portal->nodes[1])
2319                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2320                 if (portal->nodes[0] == node)
2321                         side = 0;
2322                 else if (portal->nodes[1] == node)
2323                 {
2324                         clipplane.dist = -clipplane.dist;
2325                         VectorNegate(clipplane.normal, clipplane.normal);
2326                         side = 1;
2327                 }
2328                 else
2329                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2330
2331                 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2332                 if (!nodeportalwinding)
2333                 {
2334                         Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2335                         break;
2336                 }
2337         }
2338
2339         if (nodeportalwinding)
2340         {
2341                 // if the plane was not clipped on all sides, there was an error
2342                 nodeportal->winding = nodeportalwinding;
2343                 AddPortalToNodes(nodeportal, front, back);
2344         }
2345
2346         // split the portals of this node along this node's plane and assign them to the children of this node
2347         // (migrating the portals downward through the tree)
2348         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2349         {
2350                 if (portal->nodes[0] == portal->nodes[1])
2351                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2352                 if (portal->nodes[0] == node)
2353                         side = 0;
2354                 else if (portal->nodes[1] == node)
2355                         side = 1;
2356                 else
2357                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2358                 nextportal = portal->next[side];
2359
2360                 other_node = portal->nodes[!side];
2361                 RemovePortalFromNodes(portal);
2362
2363                 // cut the portal into two portals, one on each side of the node plane
2364                 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2365
2366                 if (!frontwinding)
2367                 {
2368                         if (side == 0)
2369                                 AddPortalToNodes(portal, back, other_node);
2370                         else
2371                                 AddPortalToNodes(portal, other_node, back);
2372                         continue;
2373                 }
2374                 if (!backwinding)
2375                 {
2376                         if (side == 0)
2377                                 AddPortalToNodes(portal, front, other_node);
2378                         else
2379                                 AddPortalToNodes(portal, other_node, front);
2380                         continue;
2381                 }
2382
2383                 // the winding is split
2384                 splitportal = AllocPortal();
2385                 temp = splitportal->chain;
2386                 *splitportal = *portal;
2387                 splitportal->chain = temp;
2388                 splitportal->winding = backwinding;
2389                 Winding_Free(portal->winding);
2390                 portal->winding = frontwinding;
2391
2392                 if (side == 0)
2393                 {
2394                         AddPortalToNodes(portal, front, other_node);
2395                         AddPortalToNodes(splitportal, back, other_node);
2396                 }
2397                 else
2398                 {
2399                         AddPortalToNodes(portal, other_node, front);
2400                         AddPortalToNodes(splitportal, other_node, back);
2401                 }
2402         }
2403
2404         Mod_Q1BSP_RecursiveNodePortals(front);
2405         Mod_Q1BSP_RecursiveNodePortals(back);
2406 }
2407
2408 static void Mod_Q1BSP_MakePortals(void)
2409 {
2410         portalchain = NULL;
2411         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2412         Mod_Q1BSP_FinalizePortals();
2413 }
2414
2415 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2416 {
2417 #if 0
2418         int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2419         msurface_t *surf, *s;
2420         float *v0, *v1, *v2, *v3;
2421         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2422                 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2423         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2424         {
2425                 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)
2426                 {
2427                         if (surf->neighborsurfaces[vertnum])
2428                                 continue;
2429                         surf->neighborsurfaces[vertnum] = NULL;
2430                         for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2431                         {
2432                                 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2433                                  || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2434                                  || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2435                                  || s == surf)
2436                                         continue;
2437                                 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2438                                         if (s->neighborsurfaces[vnum] == surf)
2439                                                 break;
2440                                 if (vnum < s->poly_numverts)
2441                                         continue;
2442                                 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)
2443                                 {
2444                                         if (s->neighborsurfaces[vnum] == NULL
2445                                          && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2446                                           || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2447                                         {
2448                                                 surf->neighborsurfaces[vertnum] = s;
2449                                                 s->neighborsurfaces[vnum] = surf;
2450                                                 break;
2451                                         }
2452                                 }
2453                                 if (vnum < s->poly_numverts)
2454                                         break;
2455                         }
2456                 }
2457         }
2458 #endif
2459 }
2460
2461 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2462 {
2463         int i, j, stylecounts[256], totalcount, remapstyles[256];
2464         msurface_t *surf;
2465         memset(stylecounts, 0, sizeof(stylecounts));
2466         for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2467         {
2468                 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2469                 for (j = 0;j < MAXLIGHTMAPS;j++)
2470                         stylecounts[surf->styles[j]]++;
2471         }
2472         totalcount = 0;
2473         model->brushq1.light_styles = 0;
2474         for (i = 0;i < 255;i++)
2475         {
2476                 if (stylecounts[i])
2477                 {
2478                         remapstyles[i] = model->brushq1.light_styles++;
2479                         totalcount += stylecounts[i] + 1;
2480                 }
2481         }
2482         if (!totalcount)
2483                 return;
2484         model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2485         model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2486         model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2487         model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2488         model->brushq1.light_styles = 0;
2489         for (i = 0;i < 255;i++)
2490                 if (stylecounts[i])
2491                         model->brushq1.light_style[model->brushq1.light_styles++] = i;
2492         j = 0;
2493         for (i = 0;i < model->brushq1.light_styles;i++)
2494         {
2495                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2496                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2497         }
2498         for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2499         {
2500                 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2501                 for (j = 0;j < MAXLIGHTMAPS;j++)
2502                         if (surf->styles[j] != 255)
2503                                 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2504         }
2505         j = 0;
2506         for (i = 0;i < model->brushq1.light_styles;i++)
2507         {
2508                 *model->brushq1.light_styleupdatechains[i] = NULL;
2509                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2510                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2511         }
2512 }
2513
2514 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2515 {
2516         int i, j;
2517         for (i = 0;i < model->brushq1.numtextures;i++)
2518                 model->brushq1.pvstexturechainslength[i] = 0;
2519         for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2520         {
2521                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2522                 {
2523                         model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2524                         model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2525                 }
2526         }
2527         for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2528         {
2529                 if (model->brushq1.pvstexturechainslength[i])
2530                 {
2531                         model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2532                         j += model->brushq1.pvstexturechainslength[i] + 1;
2533                 }
2534                 else
2535                         model->brushq1.pvstexturechains[i] = NULL;
2536         }
2537         for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2538                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2539                         *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2540         for (i = 0;i < model->brushq1.numtextures;i++)
2541         {
2542                 if (model->brushq1.pvstexturechainslength[i])
2543                 {
2544                         *model->brushq1.pvstexturechains[i] = NULL;
2545                         model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2546                 }
2547         }
2548 }
2549
2550 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2551 {
2552         int i;
2553         float d;
2554
2555         while (node->contents >= 0)
2556         {
2557                 d = PlaneDiff(org, node->plane);
2558                 if (d > radius)
2559                         node = node->children[0];
2560                 else if (d < -radius)
2561                         node = node->children[1];
2562                 else
2563                 {
2564                         // go down both sides
2565                         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2566                         node = node->children[1];
2567                 }
2568         }
2569         // FIXME: code!
2570         // if this is a leaf, accumulate the pvs bits
2571         if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2572                 for (i = 0;i < pvsbytes;i++)
2573                         pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2574 }
2575
2576 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2577 //of the given point.
2578 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2579 {
2580         int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2581         bytes = min(bytes, pvsbufferlength);
2582         memset(pvsbuffer, 0, bytes);
2583         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2584         return bytes;
2585 }
2586
2587 //Returns PVS data for a given point
2588 //(note: always returns valid data, never NULL)
2589 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2590 {
2591         mnode_t *node;
2592         Mod_CheckLoaded(model);
2593         // LordHavoc: modified to start at first clip node,
2594         // in other words: first node of the (sub)model
2595         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2596         while (node->contents == 0)
2597                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2598         return ((mleaf_t *)node)->pvsdata;
2599 }
2600
2601 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2602 {
2603         vec3_t size;
2604         const hull_t *hull;
2605
2606         VectorSubtract(inmaxs, inmins, size);
2607         if (cmodel->brush.ishlbsp)
2608         {
2609                 if (size[0] < 3)
2610                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2611                 else if (size[0] <= 32)
2612                 {
2613                         if (size[2] < 54) // pick the nearest of 36 or 72
2614                                 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2615                         else
2616                                 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2617                 }
2618                 else
2619                         hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2620         }
2621         else
2622         {
2623                 if (size[0] < 3)
2624                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2625                 else if (size[0] <= 32)
2626                         hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2627                 else
2628                         hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2629         }
2630         VectorCopy(inmins, outmins);
2631         VectorAdd(inmins, hull->clip_size, outmaxs);
2632 }
2633
2634 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2635 extern void R_Model_Brush_Draw(entity_render_t *ent);
2636 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2637 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);
2638 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2639 {
2640         int i, j, k;
2641         dheader_t *header;
2642         dmodel_t *bm;
2643         mempool_t *mainmempool;
2644         char *loadname;
2645         model_t *originalloadmodel;
2646         float dist, modelyawradius, modelradius, *vec;
2647         msurface_t *surf;
2648         surfmesh_t *mesh;
2649
2650         mod->type = mod_brush;
2651
2652         header = (dheader_t *)buffer;
2653
2654         i = LittleLong(header->version);
2655         if (i != BSPVERSION && i != 30)
2656                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2657         mod->brush.ishlbsp = i == 30;
2658
2659         mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2660         mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2661         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2662         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2663         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2664         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2665         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2666         mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
2667         mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2668         mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2669         mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2670         mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2671
2672         if (loadmodel->isworldmodel)
2673         {
2674                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2675                 // until we get a texture for it...
2676                 R_ResetQuakeSky();
2677         }
2678
2679 // swap all the lumps
2680         mod_base = (qbyte *)header;
2681
2682         for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2683                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2684
2685 // load into heap
2686
2687         // store which lightmap format to use
2688         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2689
2690         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2691         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2692         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2693         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2694         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2695         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2696         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2697         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2698         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2699         Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2700         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2701         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2702         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2703         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2704         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2705
2706         if (mod->brushq1.data_compressedpvs)
2707                 Mem_Free(mod->brushq1.data_compressedpvs);
2708         mod->brushq1.data_compressedpvs = NULL;
2709         mod->brushq1.num_compressedpvs = 0;
2710
2711         Mod_Q1BSP_MakeHull0();
2712         Mod_Q1BSP_MakePortals();
2713
2714         if (developer.integer)
2715                 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);
2716
2717         mod->numframes = 2;             // regular and alternate animation
2718
2719         mainmempool = mod->mempool;
2720         loadname = mod->name;
2721
2722         Mod_Q1BSP_LoadLightList();
2723         originalloadmodel = loadmodel;
2724
2725 //
2726 // set up the submodels(FIXME: this is confusing)
2727 //
2728         for (i = 0;i < mod->brush.numsubmodels;i++)
2729         {
2730                 bm = &mod->brushq1.submodels[i];
2731
2732                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2733                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2734                 {
2735                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2736                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2737                 }
2738
2739                 mod->brushq1.firstmodelsurface = bm->firstface;
2740                 mod->brushq1.nummodelsurfaces = bm->numfaces;
2741
2742                 // this gets altered below if sky is used
2743                 mod->DrawSky = NULL;
2744                 mod->Draw = R_Model_Brush_Draw;
2745                 mod->DrawFakeShadow = NULL;
2746                 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2747                 mod->DrawLight = R_Model_Brush_DrawLight;
2748                 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2749                 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2750                 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2751                 Mod_Q1BSP_BuildPVSTextureChains(mod);
2752                 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2753                 if (mod->brushq1.nummodelsurfaces)
2754                 {
2755                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2756                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2757                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2758                         modelyawradius = 0;
2759                         modelradius = 0;
2760                         for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2761                         {
2762                                 // we only need to have a drawsky function if it is used(usually only on world model)
2763                                 if (surf->texinfo->texture->shader == &Cshader_sky)
2764                                         mod->DrawSky = R_Model_Brush_DrawSky;
2765                                 // LordHavoc: submodels always clip, even if water
2766                                 if (mod->brush.numsubmodels - 1)
2767                                         surf->flags |= SURF_SOLIDCLIP;
2768                                 // calculate bounding shapes
2769                                 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2770                                 {
2771                                         for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2772                                         {
2773                                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2774                                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2775                                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2776                                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2777                                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2778                                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2779                                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2780                                                 if (modelyawradius < dist)
2781                                                         modelyawradius = dist;
2782                                                 dist += vec[2]*vec[2];
2783                                                 if (modelradius < dist)
2784                                                         modelradius = dist;
2785                                         }
2786                                 }
2787                         }
2788                         modelyawradius = sqrt(modelyawradius);
2789                         modelradius = sqrt(modelradius);
2790                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2791                         mod->yawmins[2] = mod->normalmins[2];
2792                         mod->yawmaxs[2] = mod->normalmaxs[2];
2793                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2794                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2795                         mod->radius = modelradius;
2796                         mod->radius2 = modelradius * modelradius;
2797                 }
2798                 else
2799                 {
2800                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2801                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2802                 }
2803                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2804
2805                 mod->brushq1.visleafs = bm->visleafs;
2806
2807                 // LordHavoc: only register submodels if it is the world
2808                 // (prevents bsp models from replacing world submodels)
2809                 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2810                 {
2811                         char    name[10];
2812                         // duplicate the basic information
2813                         sprintf(name, "*%i", i+1);
2814                         loadmodel = Mod_FindName(name);
2815                         *loadmodel = *mod;
2816                         strcpy(loadmodel->name, name);
2817                         // textures and memory belong to the main model
2818                         loadmodel->texturepool = NULL;
2819                         loadmodel->mempool = NULL;
2820                         mod = loadmodel;
2821                 }
2822         }
2823
2824         loadmodel = originalloadmodel;
2825         //Mod_Q1BSP_ProcessLightList();
2826 }
2827
2828 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2829 {
2830 }
2831
2832 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2833 {
2834 /*
2835         d_t *in;
2836         m_t *out;
2837         int i, count;
2838
2839         in = (void *)(mod_base + l->fileofs);
2840         if (l->filelen % sizeof(*in))
2841                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2842         count = l->filelen / sizeof(*in);
2843         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2844
2845         loadmodel-> = out;
2846         loadmodel->num = count;
2847
2848         for (i = 0;i < count;i++, in++, out++)
2849         {
2850         }
2851 */
2852 }
2853
2854 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2855 {
2856 /*
2857         d_t *in;
2858         m_t *out;
2859         int i, count;
2860
2861         in = (void *)(mod_base + l->fileofs);
2862         if (l->filelen % sizeof(*in))
2863                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2864         count = l->filelen / sizeof(*in);
2865         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2866
2867         loadmodel-> = out;
2868         loadmodel->num = count;
2869
2870         for (i = 0;i < count;i++, in++, out++)
2871         {
2872         }
2873 */
2874 }
2875
2876 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2877 {
2878 /*
2879         d_t *in;
2880         m_t *out;
2881         int i, count;
2882
2883         in = (void *)(mod_base + l->fileofs);
2884         if (l->filelen % sizeof(*in))
2885                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2886         count = l->filelen / sizeof(*in);
2887         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2888
2889         loadmodel-> = out;
2890         loadmodel->num = count;
2891
2892         for (i = 0;i < count;i++, in++, out++)
2893         {
2894         }
2895 */
2896 }
2897
2898 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2899 {
2900 /*
2901         d_t *in;
2902         m_t *out;
2903         int i, count;
2904
2905         in = (void *)(mod_base + l->fileofs);
2906         if (l->filelen % sizeof(*in))
2907                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2908         count = l->filelen / sizeof(*in);
2909         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2910
2911         loadmodel-> = out;
2912         loadmodel->num = count;
2913
2914         for (i = 0;i < count;i++, in++, out++)
2915         {
2916         }
2917 */
2918 }
2919
2920 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2921 {
2922 /*
2923         d_t *in;
2924         m_t *out;
2925         int i, count;
2926
2927         in = (void *)(mod_base + l->fileofs);
2928         if (l->filelen % sizeof(*in))
2929                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2930         count = l->filelen / sizeof(*in);
2931         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2932
2933         loadmodel-> = out;
2934         loadmodel->num = count;
2935
2936         for (i = 0;i < count;i++, in++, out++)
2937         {
2938         }
2939 */
2940 }
2941
2942 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2943 {
2944 /*
2945         d_t *in;
2946         m_t *out;
2947         int i, count;
2948
2949         in = (void *)(mod_base + l->fileofs);
2950         if (l->filelen % sizeof(*in))
2951                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2952         count = l->filelen / sizeof(*in);
2953         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2954
2955         loadmodel-> = out;
2956         loadmodel->num = count;
2957
2958         for (i = 0;i < count;i++, in++, out++)
2959         {
2960         }
2961 */
2962 }
2963
2964 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2965 {
2966 /*
2967         d_t *in;
2968         m_t *out;
2969         int i, count;
2970
2971         in = (void *)(mod_base + l->fileofs);
2972         if (l->filelen % sizeof(*in))
2973                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2974         count = l->filelen / sizeof(*in);
2975         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2976
2977         loadmodel-> = out;
2978         loadmodel->num = count;
2979
2980         for (i = 0;i < count;i++, in++, out++)
2981         {
2982         }
2983 */
2984 }
2985
2986 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
2987 {
2988 /*
2989         d_t *in;
2990         m_t *out;
2991         int i, count;
2992
2993         in = (void *)(mod_base + l->fileofs);
2994         if (l->filelen % sizeof(*in))
2995                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2996         count = l->filelen / sizeof(*in);
2997         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2998
2999         loadmodel-> = out;
3000         loadmodel->num = count;
3001
3002         for (i = 0;i < count;i++, in++, out++)
3003         {
3004         }
3005 */
3006 }
3007
3008 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3009 {
3010 /*
3011         d_t *in;
3012         m_t *out;
3013         int i, count;
3014
3015         in = (void *)(mod_base + l->fileofs);
3016         if (l->filelen % sizeof(*in))
3017                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3018         count = l->filelen / sizeof(*in);
3019         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3020
3021         loadmodel-> = out;
3022         loadmodel->num = count;
3023
3024         for (i = 0;i < count;i++, in++, out++)
3025         {
3026         }
3027 */
3028 }
3029
3030 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3031 {
3032 /*
3033         d_t *in;
3034         m_t *out;
3035         int i, count;
3036
3037         in = (void *)(mod_base + l->fileofs);
3038         if (l->filelen % sizeof(*in))
3039                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3040         count = l->filelen / sizeof(*in);
3041         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3042
3043         loadmodel-> = out;
3044         loadmodel->num = count;
3045
3046         for (i = 0;i < count;i++, in++, out++)
3047         {
3048         }
3049 */
3050 }
3051
3052 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3053 {
3054 /*
3055         d_t *in;
3056         m_t *out;
3057         int i, count;
3058
3059         in = (void *)(mod_base + l->fileofs);
3060         if (l->filelen % sizeof(*in))
3061                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3062         count = l->filelen / sizeof(*in);
3063         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3064
3065         loadmodel-> = out;
3066         loadmodel->num = count;
3067
3068         for (i = 0;i < count;i++, in++, out++)
3069         {
3070         }
3071 */
3072 }
3073
3074 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3075 {
3076 /*
3077         d_t *in;
3078         m_t *out;
3079         int i, count;
3080
3081         in = (void *)(mod_base + l->fileofs);
3082         if (l->filelen % sizeof(*in))
3083                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3084         count = l->filelen / sizeof(*in);
3085         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3086
3087         loadmodel-> = out;
3088         loadmodel->num = count;
3089
3090         for (i = 0;i < count;i++, in++, out++)
3091         {
3092         }
3093 */
3094 }
3095
3096 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3097 {
3098 /*
3099         d_t *in;
3100         m_t *out;
3101         int i, count;
3102
3103         in = (void *)(mod_base + l->fileofs);
3104         if (l->filelen % sizeof(*in))
3105                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3106         count = l->filelen / sizeof(*in);
3107         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3108
3109         loadmodel-> = out;
3110         loadmodel->num = count;
3111
3112         for (i = 0;i < count;i++, in++, out++)
3113         {
3114         }
3115 */
3116 }
3117
3118 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3119 {
3120 /*
3121         d_t *in;
3122         m_t *out;
3123         int i, count;
3124
3125         in = (void *)(mod_base + l->fileofs);
3126         if (l->filelen % sizeof(*in))
3127                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3128         count = l->filelen / sizeof(*in);
3129         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3130
3131         loadmodel-> = out;
3132         loadmodel->num = count;
3133
3134         for (i = 0;i < count;i++, in++, out++)
3135         {
3136         }
3137 */
3138 }
3139
3140 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3141 {
3142 /*
3143         d_t *in;
3144         m_t *out;
3145         int i, count;
3146
3147         in = (void *)(mod_base + l->fileofs);
3148         if (l->filelen % sizeof(*in))
3149                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3150         count = l->filelen / sizeof(*in);
3151         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3152
3153         loadmodel-> = out;
3154         loadmodel->num = count;
3155
3156         for (i = 0;i < count;i++, in++, out++)
3157         {
3158         }
3159 */
3160 }
3161
3162 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3163 {
3164 /*
3165         d_t *in;
3166         m_t *out;
3167         int i, count;
3168
3169         in = (void *)(mod_base + l->fileofs);
3170         if (l->filelen % sizeof(*in))
3171                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3172         count = l->filelen / sizeof(*in);
3173         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3174
3175         loadmodel-> = out;
3176         loadmodel->num = count;
3177
3178         for (i = 0;i < count;i++, in++, out++)
3179         {
3180         }
3181 */
3182 }
3183
3184 static void Mod_Q2BSP_LoadModels(lump_t *l)
3185 {
3186 /*
3187         d_t *in;
3188         m_t *out;
3189         int i, count;
3190
3191         in = (void *)(mod_base + l->fileofs);
3192         if (l->filelen % sizeof(*in))
3193                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3194         count = l->filelen / sizeof(*in);
3195         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3196
3197         loadmodel-> = out;
3198         loadmodel->num = count;
3199
3200         for (i = 0;i < count;i++, in++, out++)
3201         {
3202         }
3203 */
3204 }
3205
3206 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3207 {
3208         int i;
3209         q2dheader_t *header;
3210
3211         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3212
3213         mod->type = mod_brushq2;
3214
3215         header = (q2dheader_t *)buffer;
3216
3217         i = LittleLong(header->version);
3218         if (i != Q2BSPVERSION)
3219                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3220         mod->brush.ishlbsp = false;
3221         if (loadmodel->isworldmodel)
3222         {
3223                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3224                 // until we get a texture for it...
3225                 R_ResetQuakeSky();
3226         }
3227
3228         mod_base = (qbyte *)header;
3229
3230         // swap all the lumps
3231         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3232                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3233
3234         // store which lightmap format to use
3235         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3236
3237         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3238         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3239         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3240         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3241         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3242         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3243         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3244         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3245         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3246         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3247         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3248         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3249         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3250         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3251         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3252         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3253         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3254         // LordHavoc: must go last because this makes the submodels
3255         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3256 }
3257
3258 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3259 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3260
3261 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3262 {
3263         const char *data;
3264         char key[128], value[4096];
3265         float v[3];
3266         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3267         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3268         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3269         if (!l->filelen)
3270                 return;
3271         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3272         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3273         data = loadmodel->brush.entities;
3274         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3275         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3276         {
3277                 while (1)
3278                 {
3279                         if (!COM_ParseToken(&data, false))
3280                                 break; // error
3281                         if (com_token[0] == '}')
3282                                 break; // end of worldspawn
3283                         if (com_token[0] == '_')
3284                                 strcpy(key, com_token + 1);
3285                         else
3286                                 strcpy(key, com_token);
3287                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3288                                 key[strlen(key)-1] = 0;
3289                         if (!COM_ParseToken(&data, false))
3290                                 break; // error
3291                         strcpy(value, com_token);
3292                         if (!strcmp("gridsize", key))
3293                         {
3294                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3295                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3296                         }
3297                 }
3298         }
3299 }
3300
3301 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3302 {
3303         q3dtexture_t *in;
3304         q3mtexture_t *out;
3305         int i, count;
3306
3307         in = (void *)(mod_base + l->fileofs);
3308         if (l->filelen % sizeof(*in))
3309                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3310         count = l->filelen / sizeof(*in);
3311         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3312
3313         loadmodel->brushq3.data_textures = out;
3314         loadmodel->brushq3.num_textures = count;
3315
3316         for (i = 0;i < count;i++, in++, out++)
3317         {
3318                 strncpy(out->name, in->name, sizeof(out->name) - 1);
3319                 out->surfaceflags = LittleLong(in->surfaceflags);
3320                 out->nativecontents = LittleLong(in->contents);
3321                 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3322                 out->renderflags = 0;
3323                 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3324                         out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3325
3326                 out->number = i;
3327                 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3328         }
3329 }
3330
3331 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3332 {
3333         q3dplane_t *in;
3334         mplane_t *out;
3335         int i, count;
3336
3337         in = (void *)(mod_base + l->fileofs);
3338         if (l->filelen % sizeof(*in))
3339                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3340         count = l->filelen / sizeof(*in);
3341         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3342
3343         loadmodel->brushq3.data_planes = out;
3344         loadmodel->brushq3.num_planes = count;
3345
3346         for (i = 0;i < count;i++, in++, out++)
3347         {
3348                 out->normal[0] = LittleLong(in->normal[0]);
3349                 out->normal[1] = LittleLong(in->normal[1]);
3350                 out->normal[2] = LittleLong(in->normal[2]);
3351                 out->dist = LittleLong(in->dist);
3352                 PlaneClassify(out);
3353         }
3354 }
3355
3356 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3357 {
3358         q3dbrushside_t *in;
3359         q3mbrushside_t *out;
3360         int i, n, count;
3361
3362         in = (void *)(mod_base + l->fileofs);
3363         if (l->filelen % sizeof(*in))
3364                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3365         count = l->filelen / sizeof(*in);
3366         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3367
3368         loadmodel->brushq3.data_brushsides = out;
3369         loadmodel->brushq3.num_brushsides = count;
3370
3371         for (i = 0;i < count;i++, in++, out++)
3372         {
3373                 n = LittleLong(in->planeindex);
3374                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3375                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3376                 out->plane = loadmodel->brushq3.data_planes + n;
3377                 n = LittleLong(in->textureindex);
3378                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3379                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3380                 out->texture = loadmodel->brushq3.data_textures + n;
3381         }
3382 }
3383
3384 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3385 {
3386         q3dbrush_t *in;
3387         q3mbrush_t *out;
3388         int i, j, n, c, count, maxplanes;
3389         mplane_t *planes;
3390
3391         in = (void *)(mod_base + l->fileofs);
3392         if (l->filelen % sizeof(*in))
3393                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3394         count = l->filelen / sizeof(*in);
3395         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3396
3397         loadmodel->brushq3.data_brushes = out;
3398         loadmodel->brushq3.num_brushes = count;
3399
3400         maxplanes = 0;
3401         planes = NULL;
3402
3403         for (i = 0;i < count;i++, in++, out++)
3404         {
3405                 n = LittleLong(in->firstbrushside);
3406                 c = LittleLong(in->numbrushsides);
3407                 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3408                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3409                 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3410                 out->numbrushsides = c;
3411                 n = LittleLong(in->textureindex);
3412                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3413                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3414                 out->texture = loadmodel->brushq3.data_textures + n;
3415
3416                 // make a list of mplane_t structs to construct a colbrush from
3417                 if (maxplanes < out->numbrushsides)
3418                 {
3419                         maxplanes = out->numbrushsides;
3420                         if (planes)
3421                                 Mem_Free(planes);
3422                         planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3423                 }
3424                 for (j = 0;j < out->numbrushsides;j++)
3425                 {
3426                         VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3427                         planes[j].dist = out->firstbrushside[j].plane->dist;
3428                 }
3429                 // make the colbrush from the planes
3430                 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents);
3431         }
3432         if (planes)
3433                 Mem_Free(planes);
3434 }
3435
3436 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3437 {
3438         q3deffect_t *in;
3439         q3meffect_t *out;
3440         int i, n, count;
3441
3442         in = (void *)(mod_base + l->fileofs);
3443         if (l->filelen % sizeof(*in))
3444                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3445         count = l->filelen / sizeof(*in);
3446         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3447
3448         loadmodel->brushq3.data_effects = out;
3449         loadmodel->brushq3.num_effects = count;
3450
3451         for (i = 0;i < count;i++, in++, out++)
3452         {
3453                 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3454                 n = LittleLong(in->brushindex);
3455                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3456                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3457                 out->brush = loadmodel->brushq3.data_brushes + n;
3458                 out->unknown = LittleLong(in->unknown);
3459         }
3460 }
3461
3462 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3463 {
3464         q3dvertex_t *in;
3465         int i, count;
3466
3467         in = (void *)(mod_base + l->fileofs);
3468         if (l->filelen % sizeof(*in))
3469                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3470         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3471         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3472         loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3473         loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3474         loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3475         loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3476         loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3477         loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3478
3479         for (i = 0;i < count;i++, in++)
3480         {
3481                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3482                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3483                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3484                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3485                 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3486                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3487                 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3488                 // svector/tvector are calculated later in face loading
3489                 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3490                 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3491                 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3492                 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3493                 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3494                 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3495                 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3496                 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3497                 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3498                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3499                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3500                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3501                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3502         }
3503 }
3504
3505 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3506 {
3507         int *in;
3508         int *out;
3509         int i, count;
3510
3511         in = (void *)(mod_base + l->fileofs);
3512         if (l->filelen % sizeof(int[3]))
3513                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3514         count = l->filelen / sizeof(*in);
3515         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3516
3517         loadmodel->brushq3.num_triangles = count / 3;
3518         loadmodel->brushq3.data_element3i = out;
3519         loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3520
3521         for (i = 0;i < count;i++, in++, out++)
3522         {
3523                 *out = LittleLong(*in);
3524                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3525                 {
3526                         Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3527                         *out = 0;
3528                 }
3529         }
3530 }
3531
3532 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3533 {
3534         q3dlightmap_t *in;
3535         rtexture_t **out;
3536         int i, count;
3537
3538         in = (void *)(mod_base + l->fileofs);
3539         if (l->filelen % sizeof(*in))
3540                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3541         count = l->filelen / sizeof(*in);
3542         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3543
3544         loadmodel->brushq3.data_lightmaps = out;
3545         loadmodel->brushq3.num_lightmaps = count;
3546
3547         for (i = 0;i < count;i++, in++, out++)
3548                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3549 }
3550
3551 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3552 {
3553         q3dface_t *in;
3554         q3mface_t *out;
3555         int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3556         //int *originalelement3i;
3557         //int *originalneighbor3i;
3558         float *originalvertex3f;
3559         //float *originalsvector3f;
3560         //float *originaltvector3f;
3561         //float *originalnormal3f;
3562         float *originalcolor4f;
3563         float *originaltexcoordtexture2f;
3564         float *originaltexcoordlightmap2f;
3565         float *v;
3566
3567         in = (void *)(mod_base + l->fileofs);
3568         if (l->filelen % sizeof(*in))
3569                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3570         count = l->filelen / sizeof(*in);
3571         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3572
3573         loadmodel->brushq3.data_faces = out;
3574         loadmodel->brushq3.num_faces = count;
3575
3576         for (i = 0;i < count;i++, in++, out++)
3577         {
3578                 // check face type first
3579                 out->type = LittleLong(in->type);
3580                 if (out->type != Q3FACETYPE_POLYGON
3581                  && out->type != Q3FACETYPE_PATCH
3582                  && out->type != Q3FACETYPE_MESH
3583                  && out->type != Q3FACETYPE_FLARE)
3584                 {
3585                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3586                         out->type = 0; // error
3587                         continue;
3588                 }
3589
3590                 n = LittleLong(in->textureindex);
3591                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3592                 {
3593                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3594                         out->type = 0; // error
3595                         continue;
3596                         n = 0;
3597                 }
3598                 out->texture = loadmodel->brushq3.data_textures + n;
3599                 n = LittleLong(in->effectindex);
3600                 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3601                 {
3602                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3603                         n = -1;
3604                 }
3605                 if (n == -1)
3606                         out->effect = NULL;
3607                 else
3608                         out->effect = loadmodel->brushq3.data_effects + n;
3609                 n = LittleLong(in->lightmapindex);
3610                 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3611                 {
3612                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3613                         n = -1;
3614                 }
3615                 if (n == -1)
3616                         out->lightmaptexture = NULL;
3617                 else
3618                         out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3619
3620                 out->firstvertex = LittleLong(in->firstvertex);
3621                 out->numvertices = LittleLong(in->numvertices);
3622                 out->firstelement = LittleLong(in->firstelement);
3623                 out->numelements = LittleLong(in->numelements);
3624                 out->numtriangles = out->numelements / 3;
3625                 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3626                 {
3627                         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);
3628                         out->type = 0; // error
3629                         continue;
3630                 }
3631                 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3632                 {
3633                         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);
3634                         out->type = 0; // error
3635                         continue;
3636                 }
3637                 if (out->numtriangles * 3 != out->numelements)
3638                 {
3639                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3640                         out->type = 0; // error
3641                         continue;
3642                 }
3643                 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3644                 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3645                 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3646                 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3647                 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3648                 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3649                 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3650                 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3651                 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3652                 switch(out->type)
3653                 {
3654                 case Q3FACETYPE_POLYGON:
3655                 case Q3FACETYPE_MESH:
3656                         // no processing necessary
3657                         break;
3658                 case Q3FACETYPE_PATCH:
3659                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3660                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3661                         if (patchsize[0] < 1 || patchsize[1] < 1)
3662                         {
3663                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3664                                 out->type = 0; // error
3665                                 continue;
3666                         }
3667                         // convert patch to Q3FACETYPE_MESH
3668                         xlevel = mod_q3bsp_curves_subdivide_level.integer;
3669                         ylevel = mod_q3bsp_curves_subdivide_level.integer;
3670                         finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3671                         finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3672                         finalvertices = finalwidth * finalheight;
3673                         finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3674                         originalvertex3f = out->data_vertex3f;
3675                         //originalsvector3f = out->data_svector3f;
3676                         //originaltvector3f = out->data_tvector3f;
3677                         //originalnormal3f = out->data_normal3f;
3678                         originalcolor4f = out->data_color4f;
3679                         originaltexcoordtexture2f = out->data_texcoordtexture2f;
3680                         originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3681                         //originalelement3i = out->data_element3i;
3682                         //originalneighbor3i = out->data_neighbor3i;
3683                         out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles);
3684                         out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3685                         out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3686                         out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3687                         out->data_color4f = out->data_normal3f + finalvertices * 3;
3688                         out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3689                         out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3690                         out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2);
3691                         out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3692                         out->type = Q3FACETYPE_MESH;
3693                         out->firstvertex = -1;
3694                         out->numvertices = finalvertices;
3695                         out->firstelement = -1;
3696                         out->numtriangles = finaltriangles;
3697                         out->numelements = finaltriangles * 3;
3698                         // generate geometry
3699                         // (note: normals are skipped because they get recalculated)
3700                         QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3701                         QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3702                         QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3703                         QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3704                         // generate elements
3705                         e = out->data_element3i;
3706                         for (y = 0;y < finalheight - 1;y++)
3707                         {
3708                                 row0 = (y + 0) * finalwidth;
3709                                 row1 = (y + 1) * finalwidth;
3710                                 for (x = 0;x < finalwidth - 1;x++)
3711                                 {
3712                                         *e++ = row0;
3713                                         *e++ = row1;
3714                                         *e++ = row0 + 1;
3715                                         *e++ = row1;
3716                                         *e++ = row1 + 1;
3717                                         *e++ = row0 + 1;
3718                                         row0++;
3719                                         row1++;
3720                                 }
3721                         }
3722                         out->numtriangles = Mod_RemoveDegenerateTriangles(out->numtriangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3723                         if (developer.integer)
3724                         {
3725                                 if (out->numtriangles < finaltriangles)
3726                                         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);
3727                                 else
3728                                         Con_Printf("Mod_Q3BSP_LoadFaces: %ix%i curve subdivided to %i vertices / %i triangles\n", patchsize[0], patchsize[1], out->numvertices, out->numtriangles);
3729                         }
3730                         // q3map does not put in collision brushes for curves... ugh
3731                         out->collisions = true;
3732                         break;
3733                 case Q3FACETYPE_FLARE:
3734                         Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3735                         // don't render it
3736                         out->numtriangles = 0;
3737                         break;
3738                 }
3739                 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3740                         if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3741                                 invalidelements++;
3742                 if (invalidelements)
3743                 {
3744                         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);
3745                         for (j = 0;j < out->numelements;j++)
3746                         {
3747                                 Con_Printf(" %i", out->data_element3i[j]);
3748                                 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3749                                         out->data_element3i[j] = 0;
3750                         }
3751                         Con_Printf("\n");
3752                 }
3753                 // for shadow volumes
3754                 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->numtriangles);
3755                 // for per pixel lighting
3756                 Mod_BuildTextureVectorsAndNormals(out->numvertices, out->numtriangles, out->data_vertex3f, out->data_texcoordtexture2f, out->data_element3i, out->data_svector3f, out->data_tvector3f, out->data_normal3f);
3757                 // calculate a bounding box
3758                 VectorClear(out->mins);
3759                 VectorClear(out->maxs);
3760                 if (out->numvertices)
3761                 {
3762                         VectorCopy(out->data_vertex3f, out->mins);
3763                         VectorCopy(out->data_vertex3f, out->maxs);
3764                         for (j = 1, v = out->data_vertex3f + 3;j < out->numvertices;j++, v += 3)
3765                         {
3766                                 out->mins[0] = min(out->mins[0], v[0]);
3767                                 out->maxs[0] = max(out->maxs[0], v[0]);
3768                                 out->mins[1] = min(out->mins[1], v[1]);
3769                                 out->maxs[1] = max(out->maxs[1], v[1]);
3770                                 out->mins[2] = min(out->mins[2], v[2]);
3771                                 out->maxs[2] = max(out->maxs[2], v[2]);
3772                         }
3773                         out->mins[0] -= 1.0f;
3774                         out->mins[1] -= 1.0f;
3775                         out->mins[2] -= 1.0f;
3776                         out->maxs[0] += 1.0f;
3777                         out->maxs[1] += 1.0f;
3778                         out->maxs[2] += 1.0f;
3779                 }
3780         }
3781 }
3782
3783 static void Mod_Q3BSP_LoadModels(lump_t *l)
3784 {
3785         q3dmodel_t *in;
3786         q3mmodel_t *out;
3787         int i, j, n, c, count;
3788
3789         in = (void *)(mod_base + l->fileofs);
3790         if (l->filelen % sizeof(*in))
3791                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3792         count = l->filelen / sizeof(*in);
3793         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3794
3795         loadmodel->brushq3.data_models = out;
3796         loadmodel->brushq3.num_models = count;
3797
3798         for (i = 0;i < count;i++, in++, out++)
3799         {
3800                 for (j = 0;j < 3;j++)
3801                 {
3802                         out->mins[j] = LittleFloat(in->mins[j]);
3803                         out->maxs[j] = LittleFloat(in->maxs[j]);
3804                 }
3805                 n = LittleLong(in->firstface);
3806                 c = LittleLong(in->numfaces);
3807                 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3808                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3809                 out->firstface = loadmodel->brushq3.data_faces + n;
3810                 out->numfaces = c;
3811                 n = LittleLong(in->firstbrush);
3812                 c = LittleLong(in->numbrushes);
3813                 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3814                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3815                 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3816                 out->numbrushes = c;
3817         }
3818 }
3819
3820 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3821 {
3822         int *in;
3823         q3mbrush_t **out;
3824         int i, n, count;
3825
3826         in = (void *)(mod_base + l->fileofs);
3827         if (l->filelen % sizeof(*in))
3828                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3829         count = l->filelen / sizeof(*in);
3830         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3831
3832         loadmodel->brushq3.data_leafbrushes = out;
3833         loadmodel->brushq3.num_leafbrushes = count;
3834
3835         for (i = 0;i < count;i++, in++, out++)
3836         {
3837                 n = LittleLong(*in);
3838                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3839                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3840                 *out = loadmodel->brushq3.data_brushes + n;
3841         }
3842 }
3843
3844 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3845 {
3846         int *in;
3847         q3mface_t **out;
3848         int i, n, count;
3849
3850         in = (void *)(mod_base + l->fileofs);
3851         if (l->filelen % sizeof(*in))
3852                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3853         count = l->filelen / sizeof(*in);
3854         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3855
3856         loadmodel->brushq3.data_leaffaces = out;
3857         loadmodel->brushq3.num_leaffaces = count;
3858
3859         for (i = 0;i < count;i++, in++, out++)
3860         {
3861                 n = LittleLong(*in);
3862                 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3863                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3864                 *out = loadmodel->brushq3.data_faces + n;
3865         }
3866 }
3867
3868 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3869 {
3870         q3dleaf_t *in;
3871         q3mleaf_t *out;
3872         int i, j, n, c, count;
3873
3874         in = (void *)(mod_base + l->fileofs);
3875         if (l->filelen % sizeof(*in))
3876                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3877         count = l->filelen / sizeof(*in);
3878         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3879
3880         loadmodel->brushq3.data_leafs = out;
3881         loadmodel->brushq3.num_leafs = count;
3882
3883         for (i = 0;i < count;i++, in++, out++)
3884         {
3885                 out->isnode = false;
3886                 out->parent = NULL;
3887                 out->clusterindex = LittleLong(in->clusterindex);
3888                 out->areaindex = LittleLong(in->areaindex);
3889                 for (j = 0;j < 3;j++)
3890                 {
3891                         // yes the mins/maxs are ints
3892                         out->mins[j] = LittleLong(in->mins[j]);
3893                         out->maxs[j] = LittleLong(in->maxs[j]);
3894                 }
3895                 n = LittleLong(in->firstleafface);
3896                 c = LittleLong(in->numleaffaces);
3897                 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3898                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3899                 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3900                 out->numleaffaces = c;
3901                 n = LittleLong(in->firstleafbrush);
3902                 c = LittleLong(in->numleafbrushes);
3903                 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3904                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3905                 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3906                 out->numleafbrushes = c;
3907         }
3908 }
3909
3910 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3911 {
3912         if (node->parent)
3913                 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3914         node->parent = parent;
3915         if (node->isnode)
3916         {
3917                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3918                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3919         }
3920 }
3921
3922 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3923 {
3924         q3dnode_t *in;
3925         q3mnode_t *out;
3926         int i, j, n, count;
3927
3928         in = (void *)(mod_base + l->fileofs);
3929         if (l->filelen % sizeof(*in))
3930                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3931         count = l->filelen / sizeof(*in);
3932         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3933
3934         loadmodel->brushq3.data_nodes = out;
3935         loadmodel->brushq3.num_nodes = count;
3936
3937         for (i = 0;i < count;i++, in++, out++)
3938         {
3939                 out->isnode = true;
3940                 out->parent = NULL;
3941                 n = LittleLong(in->planeindex);
3942                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3943                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3944                 out->plane = loadmodel->brushq3.data_planes + n;
3945                 for (j = 0;j < 2;j++)
3946                 {
3947                         n = LittleLong(in->childrenindex[j]);
3948                         if (n >= 0)
3949                         {
3950                                 if (n >= loadmodel->brushq3.num_nodes)
3951                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3952                                 out->children[j] = loadmodel->brushq3.data_nodes + n;
3953                         }
3954                         else
3955                         {
3956                                 n = -1 - n;
3957                                 if (n >= loadmodel->brushq3.num_leafs)
3958                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3959                                 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3960                         }
3961                 }
3962                 for (j = 0;j < 3;j++)
3963                 {
3964                         // yes the mins/maxs are ints
3965                         out->mins[j] = LittleLong(in->mins[j]);
3966                         out->maxs[j] = LittleLong(in->maxs[j]);
3967                 }
3968         }
3969
3970         // set the parent pointers
3971         Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3972 }
3973
3974 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3975 {
3976         q3dlightgrid_t *in;
3977         q3dlightgrid_t *out;
3978         int count;
3979
3980         in = (void *)(mod_base + l->fileofs);
3981         if (l->filelen % sizeof(*in))
3982                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
3983         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
3984         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
3985         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
3986         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3987         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3988         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3989         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3990         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3991         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3992         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
3993         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
3994         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
3995         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
3996         if (l->filelen < count * (int)sizeof(*in))
3997                 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]);
3998         if (l->filelen != count * (int)sizeof(*in))
3999                 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4000
4001         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4002         loadmodel->brushq3.data_lightgrid = out;
4003         loadmodel->brushq3.num_lightgrid = count;
4004
4005         // no swapping or validation necessary
4006         memcpy(out, in, count * (int)sizeof(*out));
4007
4008         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]);
4009         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]);
4010 }
4011
4012 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4013 {
4014         q3dpvs_t *in;
4015         int totalchains;
4016
4017         if (l->filelen == 0)
4018                 return;
4019
4020         in = (void *)(mod_base + l->fileofs);
4021         if (l->filelen < 9)
4022                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4023
4024         loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4025         loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4026         if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4027                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4028         totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4029         if (l->filelen < totalchains + (int)sizeof(*in))
4030                 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);
4031
4032         loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4033         memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4034 }
4035
4036 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4037 {
4038         // FIXME: finish this code
4039         VectorCopy(in, out);
4040 }
4041
4042 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4043 {
4044         int i, j, k, index[3];
4045         float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4046         q3dlightgrid_t *a, *s;
4047         // FIXME: write this
4048         if (!model->brushq3.num_lightgrid)
4049         {
4050                 ambientcolor[0] += 128;
4051                 ambientcolor[1] += 128;
4052                 ambientcolor[2] += 128;
4053                 return;
4054         }
4055         Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4056         //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4057         //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4058         transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0]);
4059         transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1]);
4060         transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2]);
4061         index[0] = (int)floor(transformed[0]);
4062         index[1] = (int)floor(transformed[1]);
4063         index[2] = (int)floor(transformed[2]);
4064         //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4065         // now lerp the values
4066         VectorClear(diffusenormal);
4067         a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4068         for (k = 0;k < 2;k++)
4069         {
4070                 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4071                 for (j = 0;j < 2;j++)
4072                 {
4073                         blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4074                         for (i = 0;i < 2;i++)
4075                         {
4076                                 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4077                                 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4078                                 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4079                                 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4080                                 pitch = s->diffusepitch * M_PI / 128;
4081                                 yaw = s->diffuseyaw * M_PI / 128;
4082                                 sinpitch = sin(pitch);
4083                                 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4084                                 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4085                                 diffusenormal[2] += blend * (cos(pitch));
4086                                 //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)));
4087                         }
4088                 }
4089         }
4090         VectorNormalize(diffusenormal);
4091         //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]);
4092 }
4093
4094 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)
4095 {
4096         int i, startside, endside;
4097         float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4098         q3mleaf_t *leaf;
4099         q3mface_t *face;
4100         colbrushf_t *brush;
4101         if (startfrac >= trace->fraction)
4102                 return;
4103         // note: all line fragments past first impact fraction are ignored
4104         while (node->isnode)
4105         {
4106                 // recurse down node sides
4107                 dist1 = PlaneDiff(start, node->plane);
4108                 dist2 = PlaneDiff(end, node->plane);
4109                 startside = dist1 < 0;
4110                 endside = dist2 < 0;
4111                 if (startside == endside)
4112                 {
4113                         // most of the time the line fragment is on one side of the plane
4114                         node = node->children[startside];
4115                 }
4116                 else
4117                 {
4118                         // line crosses node plane, split the line
4119                         midfrac = dist1 / (dist1 - dist2);
4120                         VectorLerp(linestart, midfrac, lineend, mid);
4121                         // take the near side first
4122                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4123                         if (midfrac < trace->fraction)
4124                                 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4125                         return;
4126                 }
4127         }
4128         // hit a leaf
4129         segmentmins[0] = min(start[0], end[0]);
4130         segmentmins[1] = min(start[1], end[1]);
4131         segmentmins[2] = min(start[2], end[2]);
4132         segmentmaxs[0] = max(start[0], end[0]);
4133         segmentmaxs[1] = max(start[1], end[1]);
4134         segmentmaxs[2] = max(start[2], end[2]);
4135         leaf = (q3mleaf_t *)node;
4136         for (i = 0;i < leaf->numleafbrushes;i++)
4137         {
4138                 if (startfrac >= trace->fraction)
4139                         return;
4140                 brush = leaf->firstleafbrush[i]->colbrushf;
4141                 if (brush && brush->markframe != markframe)
4142                 {
4143                         brush->markframe = markframe;
4144                         if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4145                                 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4146                 }
4147         }
4148         if (mod_q3bsp_curves_collisions.integer)
4149         {
4150                 for (i = 0;i < leaf->numleaffaces;i++)
4151                 {
4152                         if (startfrac >= trace->fraction)
4153                                 return;
4154                         face = leaf->firstleafface[i];
4155                         if (face->collisions && face->collisionmarkframe != markframe)
4156                         {
4157                                 face->collisionmarkframe = markframe;
4158                                 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4159                                         Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4160                         }
4161                 }
4162         }
4163 }
4164
4165 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)
4166 {
4167         int i, sides;
4168         float nodesegmentmins[3], nodesegmentmaxs[3];
4169         q3mleaf_t *leaf;
4170         colbrushf_t *brush;
4171         q3mface_t *face;
4172         nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4173         nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4174         nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4175         nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4176         nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4177         nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4178         if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4179                 return;
4180         if (node->isnode)
4181         {
4182                 // recurse down node sides
4183                 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4184                 if (sides == 3)
4185                 {
4186                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4187                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4188                 }
4189                 else if (sides == 2)
4190                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4191                 else // sides == 1
4192                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4193                 /*
4194                 dist = node->plane->dist - (1.0f / 8.0f);
4195                 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4196                 {
4197                         if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4198                         {
4199                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4200                                 break;
4201                         }
4202                 }
4203                 */
4204                 /*
4205                 dist = node->plane->dist + (1.0f / 8.0f);
4206                 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4207                 {
4208                         if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4209                         {
4210                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4211                                 break;
4212                         }
4213                 }
4214                 */
4215                 /*
4216                 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4217                 if (sides & 1)
4218                         Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4219                 if (sides & 2)
4220                         Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4221                 */
4222         }
4223         else
4224         {
4225                 // hit a leaf
4226                 leaf = (q3mleaf_t *)node;
4227                 for (i = 0;i < leaf->numleafbrushes;i++)
4228                 {
4229                         brush = leaf->firstleafbrush[i]->colbrushf;
4230                         if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4231                         {
4232                                 brush->markframe = markframe;
4233                                 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4234                         }
4235                 }
4236                 if (mod_q3bsp_curves_collisions.integer)
4237                 {
4238                         for (i = 0;i < leaf->numleaffaces;i++)
4239                         {
4240                                 face = leaf->firstleafface[i];
4241                                 // 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
4242                                 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4243                                         Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4244                         }
4245                 }
4246         }
4247 }
4248
4249 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)
4250 {
4251         int i;
4252         float segmentmins[3], segmentmaxs[3];
4253         colbrushf_t *thisbrush_start, *thisbrush_end;
4254         matrix4x4_t startmatrix, endmatrix;
4255         static int markframe = 0;
4256         q3mface_t *face;
4257         memset(trace, 0, sizeof(*trace));
4258         trace->fraction = 1;
4259         trace->hitsupercontentsmask = hitsupercontentsmask;
4260         Matrix4x4_CreateIdentity(&startmatrix);
4261         Matrix4x4_CreateIdentity(&endmatrix);
4262         segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4263         segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4264         segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4265         segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4266         segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4267         segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4268         if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4269         {
4270                 // line trace
4271                 if (model->brushq3.submodel)
4272                 {
4273                         for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4274                                 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4275                                         Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4276                         if (mod_q3bsp_curves_collisions.integer)
4277                         {
4278                                 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4279                                 {
4280                                         face = model->brushq3.data_thismodel->firstface + i;
4281                                         if (face->collisions)
4282                                                 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4283                                 }
4284                         }
4285                 }
4286                 else
4287                         Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4288         }
4289         else
4290         {
4291                 // box trace, performed as brush trace
4292                 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4293                 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4294                 if (model->brushq3.submodel)
4295                 {
4296                         for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4297                                 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4298                                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4299                         if (mod_q3bsp_curves_collisions.integer)
4300                         {
4301                                 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4302                                 {
4303                                         face = model->brushq3.data_thismodel->firstface + i;
4304                                         if (face->collisions)
4305                                                 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->numtriangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4306                                 }
4307                         }
4308                 }
4309                 else
4310                         Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4311         }
4312 }
4313
4314
4315 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)
4316 {
4317         int clusterindex;
4318 loc0:
4319         if (!node->isnode)
4320         {
4321                 // leaf
4322                 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4323                 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4324         }
4325
4326         // node - recurse down the BSP tree
4327         switch (BoxOnPlaneSide(mins, maxs, node->plane))
4328         {
4329         case 1: // front
4330                 node = node->children[0];
4331                 goto loc0;
4332         case 2: // back
4333                 node = node->children[1];
4334                 goto loc0;
4335         default: // crossing
4336                 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4337                         return true;
4338                 node = node->children[1];
4339                 goto loc0;
4340         }
4341         // never reached
4342         return false;
4343 }
4344
4345 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4346 {
4347         return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4348 }
4349
4350 //Returns PVS data for a given point
4351 //(note: can return NULL)
4352 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4353 {
4354         q3mnode_t *node;
4355         Mod_CheckLoaded(model);
4356         node = model->brushq3.data_nodes;
4357         while (node->isnode)
4358                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4359         if (((q3mleaf_t *)node)->clusterindex >= 0)
4360                 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4361         else
4362                 return NULL;
4363 }
4364
4365 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4366 {
4367         int i;
4368         float d;
4369         qbyte *pvs;
4370
4371         while (node->isnode)
4372         {
4373                 d = PlaneDiff(org, node->plane);
4374                 if (d > radius)
4375                         node = node->children[0];
4376                 else if (d < -radius)
4377                         node = node->children[1];
4378                 else
4379                 {
4380                         // go down both sides
4381                         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4382                         node = node->children[1];
4383                 }
4384         }
4385         // if this is a leaf with a pvs, accumulate the pvs bits
4386         if (((q3mleaf_t *)node)->clusterindex >= 0)
4387         {
4388                 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4389                 for (i = 0;i < pvsbytes;i++)
4390                         pvsbuffer[i] |= pvs[i];
4391         }
4392         return;
4393 }
4394
4395 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4396 //of the given point.
4397 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4398 {
4399         int bytes = model->brushq3.num_pvschainlength;
4400         bytes = min(bytes, pvsbufferlength);
4401         memset(pvsbuffer, 0, bytes);
4402         Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4403         return bytes;
4404 }
4405
4406
4407 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4408 {
4409         int supercontents = 0;
4410         if (nativecontents & Q2CONTENTS_SOLID)
4411                 supercontents |= SUPERCONTENTS_SOLID;
4412         if (nativecontents & Q2CONTENTS_WATER)
4413                 supercontents |= SUPERCONTENTS_WATER;
4414         if (nativecontents & Q2CONTENTS_SLIME)
4415                 supercontents |= SUPERCONTENTS_SLIME;
4416         if (nativecontents & Q2CONTENTS_LAVA)
4417                 supercontents |= SUPERCONTENTS_LAVA;
4418         return supercontents;
4419 }
4420
4421 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4422 {
4423         int nativecontents = 0;
4424         if (supercontents & SUPERCONTENTS_SOLID)
4425                 nativecontents |= Q2CONTENTS_SOLID;
4426         if (supercontents & SUPERCONTENTS_WATER)
4427                 nativecontents |= Q2CONTENTS_WATER;
4428         if (supercontents & SUPERCONTENTS_SLIME)
4429                 nativecontents |= Q2CONTENTS_SLIME;
4430         if (supercontents & SUPERCONTENTS_LAVA)
4431                 nativecontents |= Q2CONTENTS_LAVA;
4432         return nativecontents;
4433 }
4434
4435 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4436 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4437 //extern void R_Q3BSP_DrawFakeShadow(struct entity_render_s *ent);
4438 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4439 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);
4440 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4441 {
4442         int i;
4443         q3dheader_t *header;
4444         float corner[3], yawradius, modelradius;
4445
4446         mod->type = mod_brushq3;
4447         mod->numframes = 1;
4448         mod->numskins = 1;
4449
4450         header = (q3dheader_t *)buffer;
4451
4452         i = LittleLong(header->version);
4453         if (i != Q3BSPVERSION)
4454                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4455         if (loadmodel->isworldmodel)
4456         {
4457                 Cvar_SetValue("halflifebsp", false);
4458                 // until we get a texture for it...
4459                 R_ResetQuakeSky();
4460         }
4461
4462         mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4463         mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4464         mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4465         mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4466         mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4467         mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4468         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4469         mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4470         //mod->DrawSky = R_Q3BSP_DrawSky;
4471         mod->Draw = R_Q3BSP_Draw;
4472         //mod->DrawFakeShadow = R_Q3BSP_DrawFakeShadow;
4473         mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4474         mod->DrawLight = R_Q3BSP_DrawLight;
4475
4476         mod_base = (qbyte *)header;
4477
4478         // swap all the lumps
4479         for (i = 0;i < (int) sizeof(*header) / 4;i++)
4480                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4481
4482         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4483         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4484         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4485         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4486         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4487         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4488         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4489         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4490         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4491         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4492         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4493         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4494         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4495         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4496         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4497         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4498         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4499         loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4500
4501         for (i = 0;i < loadmodel->brushq3.num_models;i++)
4502         {
4503                 if (i == 0)
4504                         mod = loadmodel;
4505                 else
4506                 {
4507                         char name[10];
4508                         // LordHavoc: only register submodels if it is the world
4509                         // (prevents bsp models from replacing world submodels)
4510                         if (!loadmodel->isworldmodel)
4511                                 continue;
4512                         // duplicate the basic information
4513                         sprintf(name, "*%i", i);
4514                         mod = Mod_FindName(name);
4515                         *mod = *loadmodel;
4516                         strcpy(mod->name, name);
4517                         // textures and memory belong to the main model
4518                         mod->texturepool = NULL;
4519                         mod->mempool = NULL;
4520                 }
4521                 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4522                 mod->brushq3.submodel = i;
4523
4524                 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4525                 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4526                 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4527                 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4528                 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4529                 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4530                 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4531                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4532                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4533                 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4534                 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4535                 mod->yawmins[2] = mod->normalmins[2];
4536                 mod->yawmaxs[2] = mod->normalmaxs[2];
4537                 mod->radius = modelradius;
4538                 mod->radius2 = modelradius * modelradius;
4539         }
4540 }
4541
4542 void Mod_IBSP_Load(model_t *mod, void *buffer)
4543 {
4544         int i = LittleLong(((int *)buffer)[1]);
4545         if (i == Q3BSPVERSION)
4546                 Mod_Q3BSP_Load(mod,buffer);
4547         else if (i == Q2BSPVERSION)
4548                 Mod_Q2BSP_Load(mod,buffer);
4549         else
4550                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4551 }
4552
4553 void Mod_MAP_Load(model_t *mod, void *buffer)
4554 {
4555         Host_Error("Mod_MAP_Load: not yet implemented\n");
4556 }
4557