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