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