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