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