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