changed uses of q3mmodel_t to q3dmodel_t because there are no actual differences...
[divverent/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24 #include "polygon.h"
25 #include "curves.h"
26 #include "wad.h"
27
28
29 // note: model_shared.c sets up r_notexture, and r_surf_notexture
30
31 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
32
33 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
34 cvar_t halflifebsp = {0, "halflifebsp", "0"};
35 cvar_t r_novis = {0, "r_novis", "0"};
36 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
37 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
38 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
39 cvar_t r_subdivisions_tolerance = {0, "r_subdivisions_tolerance", "4"};
40 cvar_t r_subdivisions_mintess = {0, "r_subdivisions_mintess", "1"};
41 cvar_t r_subdivisions_maxtess = {0, "r_subdivisions_maxtess", "1024"};
42 cvar_t r_subdivisions_maxvertices = {0, "r_subdivisions_maxvertices", "65536"};
43 cvar_t r_subdivisions_collision_tolerance = {0, "r_subdivisions_collision_tolerance", "15"};
44 cvar_t r_subdivisions_collision_mintess = {0, "r_subdivisions_collision_mintess", "1"};
45 cvar_t r_subdivisions_collision_maxtess = {0, "r_subdivisions_collision_maxtess", "1024"};
46 cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225"};
47 cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"};
48 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"};
49 cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0"};
50
51 static void Mod_Q1BSP_Collision_Init (void);
52 void Mod_BrushInit(void)
53 {
54 //      Cvar_RegisterVariable(&r_subdivide_size);
55         Cvar_RegisterVariable(&halflifebsp);
56         Cvar_RegisterVariable(&r_novis);
57         Cvar_RegisterVariable(&r_miplightmaps);
58         Cvar_RegisterVariable(&r_lightmaprgba);
59         Cvar_RegisterVariable(&r_nosurftextures);
60         Cvar_RegisterVariable(&r_subdivisions_tolerance);
61         Cvar_RegisterVariable(&r_subdivisions_mintess);
62         Cvar_RegisterVariable(&r_subdivisions_maxtess);
63         Cvar_RegisterVariable(&r_subdivisions_maxvertices);
64         Cvar_RegisterVariable(&r_subdivisions_collision_tolerance);
65         Cvar_RegisterVariable(&r_subdivisions_collision_mintess);
66         Cvar_RegisterVariable(&r_subdivisions_collision_maxtess);
67         Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices);
68         Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
69         Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
70         Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
71         memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
72         Mod_Q1BSP_Collision_Init();
73 }
74
75 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
76 {
77         mnode_t *node;
78
79         if (model == NULL)
80                 return NULL;
81
82         Mod_CheckLoaded(model);
83
84         // LordHavoc: modified to start at first clip node,
85         // in other words: first node of the (sub)model
86         node = model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode;
87         while (node->plane)
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->brush.data_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 typedef struct findnonsolidlocationinfo_s
159 {
160         vec3_t center;
161         vec_t radius;
162         vec3_t nudge;
163         vec_t bestdist;
164         model_t *model;
165 }
166 findnonsolidlocationinfo_t;
167
168 #if 0
169 extern cvar_t samelevel;
170 #endif
171 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
172 {
173         int i, surfacenum, k, *tri, *mark;
174         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
175 #if 0
176         float surfnormal[3];
177 #endif
178         msurface_t *surface;
179         for (surfacenum = 0, mark = leaf->firstleafsurface;surfacenum < leaf->numleafsurfaces;surfacenum++, mark++)
180         {
181                 surface = info->model->brush.data_surfaces + *mark;
182                 if (surface->flags & SURF_SOLIDCLIP)
183                 {
184 #if 0
185                         VectorCopy(surface->plane->normal, surfnormal);
186                         if (surface->flags & SURF_PLANEBACK)
187                                 VectorNegate(surfnormal, surfnormal);
188 #endif
189                         for (k = 0;k < surface->mesh.num_triangles;k++)
190                         {
191                                 tri = surface->mesh.data_element3i + k * 3;
192                                 VectorCopy((surface->mesh.data_vertex3f + tri[0] * 3), vert[0]);
193                                 VectorCopy((surface->mesh.data_vertex3f + tri[1] * 3), vert[1]);
194                                 VectorCopy((surface->mesh.data_vertex3f + tri[2] * 3), vert[2]);
195                                 VectorSubtract(vert[1], vert[0], edge[0]);
196                                 VectorSubtract(vert[2], vert[1], edge[1]);
197                                 CrossProduct(edge[1], edge[0], facenormal);
198                                 if (facenormal[0] || facenormal[1] || facenormal[2])
199                                 {
200                                         VectorNormalize(facenormal);
201 #if 0
202                                         if (VectorDistance(facenormal, surfnormal) > 0.01f)
203                                                 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
204 #endif
205                                         f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
206                                         if (f <= info->bestdist && f >= -info->bestdist)
207                                         {
208                                                 VectorSubtract(vert[0], vert[2], edge[2]);
209                                                 VectorNormalize(edge[0]);
210                                                 VectorNormalize(edge[1]);
211                                                 VectorNormalize(edge[2]);
212                                                 CrossProduct(facenormal, edge[0], edgenormal[0]);
213                                                 CrossProduct(facenormal, edge[1], edgenormal[1]);
214                                                 CrossProduct(facenormal, edge[2], edgenormal[2]);
215 #if 0
216                                                 if (samelevel.integer & 1)
217                                                         VectorNegate(edgenormal[0], edgenormal[0]);
218                                                 if (samelevel.integer & 2)
219                                                         VectorNegate(edgenormal[1], edgenormal[1]);
220                                                 if (samelevel.integer & 4)
221                                                         VectorNegate(edgenormal[2], edgenormal[2]);
222                                                 for (i = 0;i < 3;i++)
223                                                         if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
224                                                          || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
225                                                          || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
226                                                                 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]);
227 #endif
228                                                 // face distance
229                                                 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
230                                                  && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
231                                                  && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
232                                                 {
233                                                         // we got lucky, the center is within the face
234                                                         dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
235                                                         if (dist < 0)
236                                                         {
237                                                                 dist = -dist;
238                                                                 if (info->bestdist > dist)
239                                                                 {
240                                                                         info->bestdist = dist;
241                                                                         VectorScale(facenormal, (info->radius - -dist), info->nudge);
242                                                                 }
243                                                         }
244                                                         else
245                                                         {
246                                                                 if (info->bestdist > dist)
247                                                                 {
248                                                                         info->bestdist = dist;
249                                                                         VectorScale(facenormal, (info->radius - dist), info->nudge);
250                                                                 }
251                                                         }
252                                                 }
253                                                 else
254                                                 {
255                                                         // check which edge or vertex the center is nearest
256                                                         for (i = 0;i < 3;i++)
257                                                         {
258                                                                 f = DotProduct(info->center, edge[i]);
259                                                                 if (f >= DotProduct(vert[0], edge[i])
260                                                                  && f <= DotProduct(vert[1], edge[i]))
261                                                                 {
262                                                                         // on edge
263                                                                         VectorMA(info->center, -f, edge[i], point);
264                                                                         dist = sqrt(DotProduct(point, point));
265                                                                         if (info->bestdist > dist)
266                                                                         {
267                                                                                 info->bestdist = dist;
268                                                                                 VectorScale(point, (info->radius / dist), info->nudge);
269                                                                         }
270                                                                         // skip both vertex checks
271                                                                         // (both are further away than this edge)
272                                                                         i++;
273                                                                 }
274                                                                 else
275                                                                 {
276                                                                         // not on edge, check first vertex of edge
277                                                                         VectorSubtract(info->center, vert[i], point);
278                                                                         dist = sqrt(DotProduct(point, point));
279                                                                         if (info->bestdist > dist)
280                                                                         {
281                                                                                 info->bestdist = dist;
282                                                                                 VectorScale(point, (info->radius / dist), info->nudge);
283                                                                         }
284                                                                 }
285                                                         }
286                                                 }
287                                         }
288                                 }
289                         }
290                 }
291         }
292 }
293
294 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
295 {
296         if (node->plane)
297         {
298                 float f = PlaneDiff(info->center, node->plane);
299                 if (f >= -info->bestdist)
300                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
301                 if (f <= info->bestdist)
302                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
303         }
304         else
305         {
306                 if (((mleaf_t *)node)->numleafsurfaces)
307                         Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
308         }
309 }
310
311 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
312 {
313         int i;
314         findnonsolidlocationinfo_t info;
315         if (model == NULL)
316         {
317                 VectorCopy(in, out);
318                 return;
319         }
320         VectorCopy(in, info.center);
321         info.radius = radius;
322         info.model = model;
323         i = 0;
324         do
325         {
326                 VectorClear(info.nudge);
327                 info.bestdist = radius;
328                 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode);
329                 VectorAdd(info.center, info.nudge, info.center);
330         }
331         while (info.bestdist < radius && ++i < 10);
332         VectorCopy(info.center, out);
333 }
334
335 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
336 {
337         switch(nativecontents)
338         {
339                 case CONTENTS_EMPTY:
340                         return 0;
341                 case CONTENTS_SOLID:
342                         return SUPERCONTENTS_SOLID;
343                 case CONTENTS_WATER:
344                         return SUPERCONTENTS_WATER;
345                 case CONTENTS_SLIME:
346                         return SUPERCONTENTS_SLIME;
347                 case CONTENTS_LAVA:
348                         return SUPERCONTENTS_LAVA;
349                 case CONTENTS_SKY:
350                         return SUPERCONTENTS_SKY;
351         }
352         return 0;
353 }
354
355 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
356 {
357         if (supercontents & SUPERCONTENTS_SOLID)
358                 return CONTENTS_SOLID;
359         if (supercontents & SUPERCONTENTS_SKY)
360                 return CONTENTS_SKY;
361         if (supercontents & SUPERCONTENTS_LAVA)
362                 return CONTENTS_LAVA;
363         if (supercontents & SUPERCONTENTS_SLIME)
364                 return CONTENTS_SLIME;
365         if (supercontents & SUPERCONTENTS_WATER)
366                 return CONTENTS_WATER;
367         return CONTENTS_EMPTY;
368 }
369
370 typedef struct
371 {
372         // the hull we're tracing through
373         const hull_t *hull;
374
375         // the trace structure to fill in
376         trace_t *trace;
377
378         // start, end, and end - start (in model space)
379         double start[3];
380         double end[3];
381         double dist[3];
382 }
383 RecursiveHullCheckTraceInfo_t;
384
385 // 1/32 epsilon to keep floating point happy
386 #define DIST_EPSILON (0.03125)
387
388 #define HULLCHECKSTATE_EMPTY 0
389 #define HULLCHECKSTATE_SOLID 1
390 #define HULLCHECKSTATE_DONE 2
391
392 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
393 {
394         // status variables, these don't need to be saved on the stack when
395         // recursing...  but are because this should be thread-safe
396         // (note: tracing against a bbox is not thread-safe, yet)
397         int ret;
398         mplane_t *plane;
399         double t1, t2;
400
401         // variables that need to be stored on the stack when recursing
402         dclipnode_t *node;
403         int side;
404         double midf, mid[3];
405
406         // LordHavoc: a goto!  everyone flee in terror... :)
407 loc0:
408         // check for empty
409         if (num < 0)
410         {
411                 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
412                 if (!t->trace->startfound)
413                 {
414                         t->trace->startfound = true;
415                         t->trace->startsupercontents |= num;
416                 }
417                 if (num & SUPERCONTENTS_LIQUIDSMASK)
418                         t->trace->inwater = true;
419                 if (num == 0)
420                         t->trace->inopen = true;
421                 if (num & t->trace->hitsupercontentsmask)
422                 {
423                         // if the first leaf is solid, set startsolid
424                         if (t->trace->allsolid)
425                                 t->trace->startsolid = true;
426 #if COLLISIONPARANOID >= 3
427                         Con_Print("S");
428 #endif
429                         return HULLCHECKSTATE_SOLID;
430                 }
431                 else
432                 {
433                         t->trace->allsolid = false;
434 #if COLLISIONPARANOID >= 3
435                         Con_Print("E");
436 #endif
437                         return HULLCHECKSTATE_EMPTY;
438                 }
439         }
440
441         // find the point distances
442         node = t->hull->clipnodes + num;
443
444         plane = t->hull->planes + node->planenum;
445         if (plane->type < 3)
446         {
447                 t1 = p1[plane->type] - plane->dist;
448                 t2 = p2[plane->type] - plane->dist;
449         }
450         else
451         {
452                 t1 = DotProduct (plane->normal, p1) - plane->dist;
453                 t2 = DotProduct (plane->normal, p2) - plane->dist;
454         }
455
456         if (t1 < 0)
457         {
458                 if (t2 < 0)
459                 {
460 #if COLLISIONPARANOID >= 3
461                         Con_Print("<");
462 #endif
463                         num = node->children[1];
464                         goto loc0;
465                 }
466                 side = 1;
467         }
468         else
469         {
470                 if (t2 >= 0)
471                 {
472 #if COLLISIONPARANOID >= 3
473                         Con_Print(">");
474 #endif
475                         num = node->children[0];
476                         goto loc0;
477                 }
478                 side = 0;
479         }
480
481         // the line intersects, find intersection point
482         // LordHavoc: this uses the original trace for maximum accuracy
483 #if COLLISIONPARANOID >= 3
484         Con_Print("M");
485 #endif
486         if (plane->type < 3)
487         {
488                 t1 = t->start[plane->type] - plane->dist;
489                 t2 = t->end[plane->type] - plane->dist;
490         }
491         else
492         {
493                 t1 = DotProduct (plane->normal, t->start) - plane->dist;
494                 t2 = DotProduct (plane->normal, t->end) - plane->dist;
495         }
496
497         midf = t1 / (t1 - t2);
498         midf = bound(p1f, midf, p2f);
499         VectorMA(t->start, midf, t->dist, mid);
500
501         // recurse both sides, front side first
502         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
503         // if this side is not empty, return what it is (solid or done)
504         if (ret != HULLCHECKSTATE_EMPTY)
505                 return ret;
506
507         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
508         // if other side is not solid, return what it is (empty or done)
509         if (ret != HULLCHECKSTATE_SOLID)
510                 return ret;
511
512         // front is air and back is solid, this is the impact point...
513         if (side)
514         {
515                 t->trace->plane.dist = -plane->dist;
516                 VectorNegate (plane->normal, t->trace->plane.normal);
517         }
518         else
519         {
520                 t->trace->plane.dist = plane->dist;
521                 VectorCopy (plane->normal, t->trace->plane.normal);
522         }
523
524         // calculate the true fraction
525         t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist;
526         t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist;
527         midf = t1 / (t1 - t2);
528         t->trace->realfraction = bound(0, midf, 1);
529
530         // calculate the return fraction which is nudged off the surface a bit
531         midf = (t1 - DIST_EPSILON) / (t1 - t2);
532         t->trace->fraction = bound(0, midf, 1);
533
534 #if COLLISIONPARANOID >= 3
535         Con_Print("D");
536 #endif
537         return HULLCHECKSTATE_DONE;
538 }
539
540 #if COLLISIONPARANOID < 2
541 static int Mod_Q1BSP_RecursiveHullCheckPoint(RecursiveHullCheckTraceInfo_t *t, int num)
542 {
543         while (num >= 0)
544                 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];
545         num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
546         t->trace->startsupercontents |= num;
547         if (num & SUPERCONTENTS_LIQUIDSMASK)
548                 t->trace->inwater = true;
549         if (num == 0)
550                 t->trace->inopen = true;
551         if (num & t->trace->hitsupercontentsmask)
552         {
553                 t->trace->allsolid = t->trace->startsolid = true;
554                 return HULLCHECKSTATE_SOLID;
555         }
556         else
557         {
558                 t->trace->allsolid = t->trace->startsolid = false;
559                 return HULLCHECKSTATE_EMPTY;
560         }
561 }
562 #endif
563
564 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)
565 {
566         // this function currently only supports same size start and end
567         double boxsize[3];
568         RecursiveHullCheckTraceInfo_t rhc;
569
570         memset(&rhc, 0, sizeof(rhc));
571         memset(trace, 0, sizeof(trace_t));
572         rhc.trace = trace;
573         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
574         rhc.trace->fraction = 1;
575         rhc.trace->realfraction = 1;
576         rhc.trace->allsolid = true;
577         VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
578         if (boxsize[0] < 3)
579                 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
580         else if (model->brush.ishlbsp)
581         {
582                 // LordHavoc: this has to have a minor tolerance (the .1) because of
583                 // minor float precision errors from the box being transformed around
584                 if (boxsize[0] < 32.1)
585                 {
586                         if (boxsize[2] < 54) // pick the nearest of 36 or 72
587                                 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
588                         else
589                                 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
590                 }
591                 else
592                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
593         }
594         else
595         {
596                 // LordHavoc: this has to have a minor tolerance (the .1) because of
597                 // minor float precision errors from the box being transformed around
598                 if (boxsize[0] < 32.1)
599                         rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
600                 else
601                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
602         }
603         VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
604         VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
605         VectorSubtract(rhc.end, rhc.start, rhc.dist);
606 #if COLLISIONPARANOID >= 2
607         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]);
608         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
609         Con_Print("\n");
610 #else
611         if (DotProduct(rhc.dist, rhc.dist))
612                 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
613         else
614                 Mod_Q1BSP_RecursiveHullCheckPoint(&rhc, rhc.hull->firstclipnode);
615 #endif
616 }
617
618 static hull_t box_hull;
619 static dclipnode_t box_clipnodes[6];
620 static mplane_t box_planes[6];
621
622 static void Mod_Q1BSP_Collision_Init (void)
623 {
624         int             i;
625         int             side;
626
627         //Set up the planes and clipnodes so that the six floats of a bounding box
628         //can just be stored out and get a proper hull_t structure.
629
630         box_hull.clipnodes = box_clipnodes;
631         box_hull.planes = box_planes;
632         box_hull.firstclipnode = 0;
633         box_hull.lastclipnode = 5;
634
635         for (i = 0;i < 6;i++)
636         {
637                 box_clipnodes[i].planenum = i;
638
639                 side = i&1;
640
641                 box_clipnodes[i].children[side] = CONTENTS_EMPTY;
642                 if (i != 5)
643                         box_clipnodes[i].children[side^1] = i + 1;
644                 else
645                         box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
646
647                 box_planes[i].type = i>>1;
648                 box_planes[i].normal[i>>1] = 1;
649         }
650 }
651
652 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)
653 {
654 #if 1
655         colbrushf_t cbox;
656         colplanef_t cbox_planes[6];
657         cbox.supercontents = boxsupercontents;
658         cbox.numplanes = 6;
659         cbox.numpoints = 0;
660         cbox.numtriangles = 0;
661         cbox.planes = cbox_planes;
662         cbox.points = NULL;
663         cbox.elements = NULL;
664         cbox.markframe = 0;
665         cbox.mins[0] = 0;
666         cbox.mins[1] = 0;
667         cbox.mins[2] = 0;
668         cbox.maxs[0] = 0;
669         cbox.maxs[1] = 0;
670         cbox.maxs[2] = 0;
671         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];
672         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];
673         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];
674         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];
675         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];
676         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];
677         memset(trace, 0, sizeof(trace_t));
678         trace->hitsupercontentsmask = hitsupercontentsmask;
679         trace->fraction = 1;
680         trace->realfraction = 1;
681         Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox);
682 #else
683         RecursiveHullCheckTraceInfo_t rhc;
684         // fill in a default trace
685         memset(&rhc, 0, sizeof(rhc));
686         memset(trace, 0, sizeof(trace_t));
687         //To keep everything totally uniform, bounding boxes are turned into small
688         //BSP trees instead of being compared directly.
689         // create a temp hull from bounding box sizes
690         box_planes[0].dist = cmaxs[0] - mins[0];
691         box_planes[1].dist = cmins[0] - maxs[0];
692         box_planes[2].dist = cmaxs[1] - mins[1];
693         box_planes[3].dist = cmins[1] - maxs[1];
694         box_planes[4].dist = cmaxs[2] - mins[2];
695         box_planes[5].dist = cmins[2] - maxs[2];
696 #if COLLISIONPARANOID >= 3
697         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]);
698 #endif
699         // trace a line through the generated clipping hull
700         //rhc.boxsupercontents = boxsupercontents;
701         rhc.hull = &box_hull;
702         rhc.trace = trace;
703         rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
704         rhc.trace->fraction = 1;
705         rhc.trace->realfraction = 1;
706         rhc.trace->allsolid = true;
707         VectorCopy(start, rhc.start);
708         VectorCopy(end, rhc.end);
709         VectorSubtract(rhc.end, rhc.start, rhc.dist);
710         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
711         //VectorMA(rhc.start, rhc.trace->fraction, rhc.dist, rhc.trace->endpos);
712         if (rhc.trace->startsupercontents)
713                 rhc.trace->startsupercontents = boxsupercontents;
714 #endif
715 }
716
717 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)
718 {
719         int side, distz = endz - startz;
720         float front, back;
721         float mid;
722
723 loc0:
724         if (!node->plane)
725                 return false;           // didn't hit anything
726
727         switch (node->plane->type)
728         {
729         case PLANE_X:
730                 node = node->children[x < node->plane->dist];
731                 goto loc0;
732         case PLANE_Y:
733                 node = node->children[y < node->plane->dist];
734                 goto loc0;
735         case PLANE_Z:
736                 side = startz < node->plane->dist;
737                 if ((endz < node->plane->dist) == side)
738                 {
739                         node = node->children[side];
740                         goto loc0;
741                 }
742                 // found an intersection
743                 mid = node->plane->dist;
744                 break;
745         default:
746                 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
747                 front += startz * node->plane->normal[2];
748                 back += endz * node->plane->normal[2];
749                 side = front < node->plane->dist;
750                 if ((back < node->plane->dist) == side)
751                 {
752                         node = node->children[side];
753                         goto loc0;
754                 }
755                 // found an intersection
756                 mid = startz + distz * (front - node->plane->dist) / (front - back);
757                 break;
758         }
759
760         // go down front side
761         if (node->children[side]->plane && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
762                 return true;    // hit something
763         else
764         {
765                 // check for impact on this node
766                 if (node->numsurfaces)
767                 {
768                         int i, ds, dt;
769                         msurface_t *surface;
770
771                         surface = r_refdef.worldmodel->brush.data_surfaces + node->firstsurface;
772                         for (i = 0;i < node->numsurfaces;i++, surface++)
773                         {
774                                 if (!(surface->texture->flags & SURF_LIGHTMAP) || !surface->samples)
775                                         continue;       // no lightmaps
776
777                                 ds = (int) (x * surface->texinfo->vecs[0][0] + y * surface->texinfo->vecs[0][1] + mid * surface->texinfo->vecs[0][2] + surface->texinfo->vecs[0][3]) - surface->texturemins[0];
778                                 dt = (int) (x * surface->texinfo->vecs[1][0] + y * surface->texinfo->vecs[1][1] + mid * surface->texinfo->vecs[1][2] + surface->texinfo->vecs[1][3]) - surface->texturemins[1];
779
780                                 if (ds >= 0 && ds < surface->extents[0] && dt >= 0 && dt < surface->extents[1])
781                                 {
782                                         qbyte *lightmap;
783                                         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;
784                                         lmwidth = ((surface->extents[0]>>4)+1);
785                                         lmheight = ((surface->extents[1]>>4)+1);
786                                         line3 = lmwidth * 3; // LordHavoc: *3 for colored lighting
787                                         size3 = lmwidth * lmheight * 3; // LordHavoc: *3 for colored lighting
788
789                                         lightmap = surface->samples + ((dt>>4) * lmwidth + (ds>>4))*3; // LordHavoc: *3 for colored lighting
790
791                                         for (maps = 0;maps < MAXLIGHTMAPS && surface->styles[maps] != 255;maps++)
792                                         {
793                                                 scale = d_lightstylevalue[surface->styles[maps]];
794                                                 r00 += lightmap[      0] * scale;g00 += lightmap[      1] * scale;b00 += lightmap[      2] * scale;
795                                                 r01 += lightmap[      3] * scale;g01 += lightmap[      4] * scale;b01 += lightmap[      5] * scale;
796                                                 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
797                                                 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
798                                                 lightmap += size3;
799                                         }
800
801 /*
802 LordHavoc: here's the readable version of the interpolation
803 code, not quite as easy for the compiler to optimize...
804
805 dsfrac is the X position in the lightmap pixel, * 16
806 dtfrac is the Y position in the lightmap pixel, * 16
807 r00 is top left corner, r01 is top right corner
808 r10 is bottom left corner, r11 is bottom right corner
809 g and b are the same layout.
810 r0 and r1 are the top and bottom intermediate results
811
812 first we interpolate the top two points, to get the top
813 edge sample
814
815         r0 = (((r01-r00) * dsfrac) >> 4) + r00;
816         g0 = (((g01-g00) * dsfrac) >> 4) + g00;
817         b0 = (((b01-b00) * dsfrac) >> 4) + b00;
818
819 then we interpolate the bottom two points, to get the
820 bottom edge sample
821
822         r1 = (((r11-r10) * dsfrac) >> 4) + r10;
823         g1 = (((g11-g10) * dsfrac) >> 4) + g10;
824         b1 = (((b11-b10) * dsfrac) >> 4) + b10;
825
826 then we interpolate the top and bottom samples to get the
827 middle sample (the one which was requested)
828
829         r = (((r1-r0) * dtfrac) >> 4) + r0;
830         g = (((g1-g0) * dtfrac) >> 4) + g0;
831         b = (((b1-b0) * dtfrac) >> 4) + b0;
832 */
833
834                                         ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
835                                         ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
836                                         ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
837                                         return true; // success
838                                 }
839                         }
840                 }
841
842                 // go down back side
843                 node = node->children[side ^ 1];
844                 startz = mid;
845                 distz = endz - startz;
846                 goto loc0;
847         }
848 }
849
850 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
851 {
852         Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
853 }
854
855 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
856 {
857         int c;
858         qbyte *outstart = out;
859         while (out < outend)
860         {
861                 if (in == inend)
862                 {
863                         Con_Printf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
864                         return;
865                 }
866                 c = *in++;
867                 if (c)
868                         *out++ = c;
869                 else
870                 {
871                         if (in == inend)
872                         {
873                                 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);
874                                 return;
875                         }
876                         for (c = *in++;c > 0;c--)
877                         {
878                                 if (out == outend)
879                                 {
880                                         Con_Printf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\" (decompressed %i of %i output bytes)\n", loadmodel->name, out - outstart, outend - outstart);
881                                         return;
882                                 }
883                                 *out++ = 0;
884                         }
885                 }
886         }
887 }
888
889 /*
890 =============
891 R_Q1BSP_LoadSplitSky
892
893 A sky texture is 256*128, with the right side being a masked overlay
894 ==============
895 */
896 void R_Q1BSP_LoadSplitSky (qbyte *src, int width, int height, int bytesperpixel)
897 {
898         int i, j;
899         unsigned solidpixels[128*128], alphapixels[128*128];
900
901         // if sky isn't the right size, just use it as a solid layer
902         if (width != 256 || height != 128)
903         {
904                 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);
905                 loadmodel->brush.alphaskytexture = NULL;;
906                 return;
907         }
908
909         if (bytesperpixel == 4)
910         {
911                 for (i = 0;i < 128;i++)
912                 {
913                         for (j = 0;j < 128;j++)
914                         {
915                                 solidpixels[(i*128) + j] = ((unsigned *)src)[i*256+j+128];
916                                 alphapixels[(i*128) + j] = ((unsigned *)src)[i*256+j];
917                         }
918                 }
919         }
920         else
921         {
922                 // make an average value for the back to avoid
923                 // a fringe on the top level
924                 int p, r, g, b;
925                 union
926                 {
927                         unsigned int i;
928                         unsigned char b[4];
929                 }
930                 rgba;
931                 r = g = b = 0;
932                 for (i = 0;i < 128;i++)
933                 {
934                         for (j = 0;j < 128;j++)
935                         {
936                                 rgba.i = palette_complete[src[i*256 + j + 128]];
937                                 r += rgba.b[0];
938                                 g += rgba.b[1];
939                                 b += rgba.b[2];
940                         }
941                 }
942                 rgba.b[0] = r/(128*128);
943                 rgba.b[1] = g/(128*128);
944                 rgba.b[2] = b/(128*128);
945                 rgba.b[3] = 0;
946                 for (i = 0;i < 128;i++)
947                 {
948                         for (j = 0;j < 128;j++)
949                         {
950                                 solidpixels[(i*128) + j] = palette_complete[src[i*256 + j + 128]];
951                                 alphapixels[(i*128) + j] = (p = src[i*256 + j]) ? palette_complete[p] : rgba.i;
952                         }
953                 }
954         }
955
956         loadmodel->brush.solidskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_solidtexture", 128, 128, (qbyte *) solidpixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
957         loadmodel->brush.alphaskytexture = R_LoadTexture2D(loadmodel->texturepool, "sky_alphatexture", 128, 128, (qbyte *) alphapixels, TEXTYPE_RGBA, TEXF_ALPHA | TEXF_PRECACHE, NULL);
958 }
959
960 static void Mod_Q1BSP_LoadTextures(lump_t *l)
961 {
962         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
963         miptex_t *dmiptex;
964         texture_t *tx, *tx2, *anims[10], *altanims[10];
965         dmiptexlump_t *m;
966         qbyte *data, *mtdata;
967         char name[256];
968
969         loadmodel->brush.data_textures = NULL;
970
971         // add two slots for notexture walls and notexture liquids
972         if (l->filelen)
973         {
974                 m = (dmiptexlump_t *)(mod_base + l->fileofs);
975                 m->nummiptex = LittleLong (m->nummiptex);
976                 loadmodel->brush.num_textures = m->nummiptex + 2;
977         }
978         else
979         {
980                 m = NULL;
981                 loadmodel->brush.num_textures = 2;
982         }
983
984         loadmodel->brush.data_textures = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_textures * sizeof(texture_t));
985
986         // fill out all slots with notexture
987         for (i = 0, tx = loadmodel->brush.data_textures;i < loadmodel->brush.num_textures;i++, tx++)
988         {
989                 strcpy(tx->name, "NO TEXTURE FOUND");
990                 tx->width = 16;
991                 tx->height = 16;
992                 tx->skin.base = r_notexture;
993                 if (i == loadmodel->brush.num_textures - 1)
994                         tx->flags = SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
995                 else
996                         tx->flags = SURF_LIGHTMAP | SURF_SOLIDCLIP;
997                 tx->currentframe = tx;
998         }
999
1000         if (!m)
1001                 return;
1002
1003         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
1004         dofs = m->dataofs;
1005         // LordHavoc: mostly rewritten map texture loader
1006         for (i = 0;i < m->nummiptex;i++)
1007         {
1008                 dofs[i] = LittleLong(dofs[i]);
1009                 if (dofs[i] == -1 || r_nosurftextures.integer)
1010                         continue;
1011                 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
1012
1013                 // make sure name is no more than 15 characters
1014                 for (j = 0;dmiptex->name[j] && j < 15;j++)
1015                         name[j] = dmiptex->name[j];
1016                 name[j] = 0;
1017
1018                 mtwidth = LittleLong(dmiptex->width);
1019                 mtheight = LittleLong(dmiptex->height);
1020                 mtdata = NULL;
1021                 j = LittleLong(dmiptex->offsets[0]);
1022                 if (j)
1023                 {
1024                         // texture included
1025                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
1026                         {
1027                                 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
1028                                 continue;
1029                         }
1030                         mtdata = (qbyte *)dmiptex + j;
1031                 }
1032
1033                 if ((mtwidth & 15) || (mtheight & 15))
1034                         Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned\n", dmiptex->name, loadmodel->name);
1035
1036                 // LordHavoc: force all names to lowercase
1037                 for (j = 0;name[j];j++)
1038                         if (name[j] >= 'A' && name[j] <= 'Z')
1039                                 name[j] += 'a' - 'A';
1040
1041                 tx = loadmodel->brush.data_textures + i;
1042                 strcpy(tx->name, name);
1043                 tx->width = mtwidth;
1044                 tx->height = mtheight;
1045
1046                 if (!tx->name[0])
1047                 {
1048                         sprintf(tx->name, "unnamed%i", i);
1049                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
1050                 }
1051
1052                 // LordHavoc: HL sky textures are entirely different than quake
1053                 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
1054                 {
1055                         if (loadmodel->isworldmodel)
1056                         {
1057                                 data = loadimagepixels(tx->name, false, 0, 0);
1058                                 if (data)
1059                                 {
1060                                         R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
1061                                         Mem_Free(data);
1062                                 }
1063                                 else if (mtdata != NULL)
1064                                         R_Q1BSP_LoadSplitSky(mtdata, mtwidth, mtheight, 1);
1065                         }
1066                 }
1067                 else
1068                 {
1069                         if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true, true))
1070                         {
1071                                 // did not find external texture, load it from the bsp or wad3
1072                                 if (loadmodel->brush.ishlbsp)
1073                                 {
1074                                         // internal texture overrides wad
1075                                         qbyte *pixels, *freepixels, *fogpixels;
1076                                         pixels = freepixels = NULL;
1077                                         if (mtdata)
1078                                                 pixels = W_ConvertWAD3Texture(dmiptex);
1079                                         if (pixels == NULL)
1080                                                 pixels = freepixels = W_GetTexture(tx->name);
1081                                         if (pixels != NULL)
1082                                         {
1083                                                 tx->width = image_width;
1084                                                 tx->height = image_height;
1085                                                 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);
1086                                                 if (Image_CheckAlpha(pixels, image_width * image_height, true))
1087                                                 {
1088                                                         fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
1089                                                         for (j = 0;j < image_width * image_height * 4;j += 4)
1090                                                         {
1091                                                                 fogpixels[j + 0] = 255;
1092                                                                 fogpixels[j + 1] = 255;
1093                                                                 fogpixels[j + 2] = 255;
1094                                                                 fogpixels[j + 3] = pixels[j + 3];
1095                                                         }
1096                                                         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);
1097                                                         Mem_Free(fogpixels);
1098                                                 }
1099                                         }
1100                                         if (freepixels)
1101                                                 Mem_Free(freepixels);
1102                                 }
1103                                 else if (mtdata) // texture included
1104                                         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);
1105                         }
1106                 }
1107                 if (tx->skin.base == NULL)
1108                 {
1109                         // no texture found
1110                         tx->width = 16;
1111                         tx->height = 16;
1112                         tx->skin.base = r_notexture;
1113                 }
1114
1115                 if (tx->name[0] == '*')
1116                 {
1117                         // turb does not block movement
1118                         tx->flags = SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
1119                         // LordHavoc: some turbulent textures should be fullbright and solid
1120                         if (!strncmp(tx->name,"*lava",5)
1121                          || !strncmp(tx->name,"*teleport",9)
1122                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
1123                                 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
1124                         else
1125                                 tx->flags |= SURF_WATERALPHA;
1126                 }
1127                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
1128                         tx->flags = SURF_DRAWSKY | SURF_SOLIDCLIP;
1129                 else
1130                         tx->flags = SURF_LIGHTMAP | SURF_SOLIDCLIP;
1131
1132                 // start out with no animation
1133                 tx->currentframe = tx;
1134         }
1135
1136         // sequence the animations
1137         for (i = 0;i < m->nummiptex;i++)
1138         {
1139                 tx = loadmodel->brush.data_textures + i;
1140                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
1141                         continue;
1142                 if (tx->anim_total[0] || tx->anim_total[1])
1143                         continue;       // already sequenced
1144
1145                 // find the number of frames in the animation
1146                 memset(anims, 0, sizeof(anims));
1147                 memset(altanims, 0, sizeof(altanims));
1148
1149                 for (j = i;j < m->nummiptex;j++)
1150                 {
1151                         tx2 = loadmodel->brush.data_textures + j;
1152                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
1153                                 continue;
1154
1155                         num = tx2->name[1];
1156                         if (num >= '0' && num <= '9')
1157                                 anims[num - '0'] = tx2;
1158                         else if (num >= 'a' && num <= 'j')
1159                                 altanims[num - 'a'] = tx2;
1160                         else
1161                                 Con_Printf("Bad animating texture %s\n", tx->name);
1162                 }
1163
1164                 max = altmax = 0;
1165                 for (j = 0;j < 10;j++)
1166                 {
1167                         if (anims[j])
1168                                 max = j + 1;
1169                         if (altanims[j])
1170                                 altmax = j + 1;
1171                 }
1172                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
1173
1174                 incomplete = false;
1175                 for (j = 0;j < max;j++)
1176                 {
1177                         if (!anims[j])
1178                         {
1179                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
1180                                 incomplete = true;
1181                         }
1182                 }
1183                 for (j = 0;j < altmax;j++)
1184                 {
1185                         if (!altanims[j])
1186                         {
1187                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
1188                                 incomplete = true;
1189                         }
1190                 }
1191                 if (incomplete)
1192                         continue;
1193
1194                 if (altmax < 1)
1195                 {
1196                         // if there is no alternate animation, duplicate the primary
1197                         // animation into the alternate
1198                         altmax = max;
1199                         for (k = 0;k < 10;k++)
1200                                 altanims[k] = anims[k];
1201                 }
1202
1203                 // link together the primary animation
1204                 for (j = 0;j < max;j++)
1205                 {
1206                         tx2 = anims[j];
1207                         tx2->animated = true;
1208                         tx2->anim_total[0] = max;
1209                         tx2->anim_total[1] = altmax;
1210                         for (k = 0;k < 10;k++)
1211                         {
1212                                 tx2->anim_frames[0][k] = anims[k];
1213                                 tx2->anim_frames[1][k] = altanims[k];
1214                         }
1215                 }
1216
1217                 // if there really is an alternate anim...
1218                 if (anims[0] != altanims[0])
1219                 {
1220                         // link together the alternate animation
1221                         for (j = 0;j < altmax;j++)
1222                         {
1223                                 tx2 = altanims[j];
1224                                 tx2->animated = true;
1225                                 // the primary/alternate are reversed here
1226                                 tx2->anim_total[0] = altmax;
1227                                 tx2->anim_total[1] = max;
1228                                 for (k = 0;k < 10;k++)
1229                                 {
1230                                         tx2->anim_frames[0][k] = altanims[k];
1231                                         tx2->anim_frames[1][k] = anims[k];
1232                                 }
1233                         }
1234                 }
1235         }
1236 }
1237
1238 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1239 {
1240         int i;
1241         qbyte *in, *out, *data, d;
1242         char litfilename[1024];
1243         loadmodel->brushq1.lightdata = NULL;
1244         if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1245         {
1246                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1247                 for (i=0; i<l->filelen; i++)
1248                         loadmodel->brushq1.lightdata[i] = mod_base[l->fileofs+i] >>= 1;
1249         }
1250         else // LordHavoc: bsp version 29 (normal white lighting)
1251         {
1252                 // LordHavoc: hope is not lost yet, check for a .lit file to load
1253                 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1254                 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1255                 strlcat (litfilename, ".lit", sizeof (litfilename));
1256                 data = (qbyte*) FS_LoadFile(litfilename, tempmempool, false);
1257                 if (data)
1258                 {
1259                         if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1260                         {
1261                                 i = LittleLong(((int *)data)[1]);
1262                                 if (i == 1)
1263                                 {
1264                                         Con_DPrintf("loaded %s\n", litfilename);
1265                                         loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1266                                         memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1267                                         Mem_Free(data);
1268                                         return;
1269                                 }
1270                                 else
1271                                 {
1272                                         Con_Printf("Unknown .lit file version (%d)\n", i);
1273                                         Mem_Free(data);
1274                                 }
1275                         }
1276                         else
1277                         {
1278                                 if (fs_filesize == 8)
1279                                         Con_Print("Empty .lit file, ignoring\n");
1280                                 else
1281                                         Con_Print("Corrupt .lit file (old version?), ignoring\n");
1282                                 Mem_Free(data);
1283                         }
1284                 }
1285                 // LordHavoc: oh well, expand the white lighting data
1286                 if (!l->filelen)
1287                         return;
1288                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1289                 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1290                 out = loadmodel->brushq1.lightdata;
1291                 memcpy(in, mod_base + l->fileofs, l->filelen);
1292                 for (i = 0;i < l->filelen;i++)
1293                 {
1294                         d = *in++;
1295                         *out++ = d;
1296                         *out++ = d;
1297                         *out++ = d;
1298                 }
1299         }
1300 }
1301
1302 static void Mod_Q1BSP_LoadLightList(void)
1303 {
1304         int a, n, numlights;
1305         char tempchar, *s, *t, *lightsstring, lightsfilename[1024];
1306         mlight_t *e;
1307
1308         strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1309         FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1310         strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1311         s = lightsstring = (char *) FS_LoadFile(lightsfilename, tempmempool, false);
1312         if (s)
1313         {
1314                 numlights = 0;
1315                 while (*s)
1316                 {
1317                         while (*s && *s != '\n' && *s != '\r')
1318                                 s++;
1319                         if (!*s)
1320                         {
1321                                 Mem_Free(lightsstring);
1322                                 Con_Printf("lights file must end with a newline\n");
1323                                 return;
1324                         }
1325                         s++;
1326                         numlights++;
1327                 }
1328                 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1329                 s = lightsstring;
1330                 n = 0;
1331                 while (*s && n < numlights)
1332                 {
1333                         t = s;
1334                         while (*s && *s != '\n' && *s != '\r')
1335                                 s++;
1336                         if (!*s)
1337                         {
1338                                 Con_Printf("misparsed lights file!\n");
1339                                 break;
1340                         }
1341                         e = loadmodel->brushq1.lights + n;
1342                         tempchar = *s;
1343                         *s = 0;
1344                         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);
1345                         *s = tempchar;
1346                         if (a != 14)
1347                         {
1348                                 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);
1349                                 break;
1350                         }
1351                         if (*s == '\r')
1352                                 s++;
1353                         if (*s == '\n')
1354                                 s++;
1355                         n++;
1356                 }
1357                 if (*s)
1358                         Con_Printf("misparsed lights file!\n");
1359                 loadmodel->brushq1.numlights = numlights;
1360                 Mem_Free(lightsstring);
1361         }
1362 }
1363
1364 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1365 {
1366         loadmodel->brushq1.num_compressedpvs = 0;
1367         loadmodel->brushq1.data_compressedpvs = NULL;
1368         if (!l->filelen)
1369                 return;
1370         loadmodel->brushq1.num_compressedpvs = l->filelen;
1371         loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1372         memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1373 }
1374
1375 // used only for HalfLife maps
1376 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1377 {
1378         char key[128], value[4096];
1379         char wadname[128];
1380         int i, j, k;
1381         if (!data)
1382                 return;
1383         if (!COM_ParseToken(&data, false))
1384                 return; // error
1385         if (com_token[0] != '{')
1386                 return; // error
1387         while (1)
1388         {
1389                 if (!COM_ParseToken(&data, false))
1390                         return; // error
1391                 if (com_token[0] == '}')
1392                         break; // end of worldspawn
1393                 if (com_token[0] == '_')
1394                         strcpy(key, com_token + 1);
1395                 else
1396                         strcpy(key, com_token);
1397                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1398                         key[strlen(key)-1] = 0;
1399                 if (!COM_ParseToken(&data, false))
1400                         return; // error
1401                 strcpy(value, com_token);
1402                 if (!strcmp("wad", key)) // for HalfLife maps
1403                 {
1404                         if (loadmodel->brush.ishlbsp)
1405                         {
1406                                 j = 0;
1407                                 for (i = 0;i < 4096;i++)
1408                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1409                                                 break;
1410                                 if (value[i])
1411                                 {
1412                                         for (;i < 4096;i++)
1413                                         {
1414                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1415                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1416                                                         j = i+1;
1417                                                 else if (value[i] == ';' || value[i] == 0)
1418                                                 {
1419                                                         k = value[i];
1420                                                         value[i] = 0;
1421                                                         strcpy(wadname, "textures/");
1422                                                         strcat(wadname, &value[j]);
1423                                                         W_LoadTextureWadFile(wadname, false);
1424                                                         j = i+1;
1425                                                         if (!k)
1426                                                                 break;
1427                                                 }
1428                                         }
1429                                 }
1430                         }
1431                 }
1432         }
1433 }
1434
1435 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1436 {
1437         loadmodel->brush.entities = NULL;
1438         if (!l->filelen)
1439                 return;
1440         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1441         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1442         if (loadmodel->brush.ishlbsp)
1443                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1444 }
1445
1446
1447 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1448 {
1449         dvertex_t       *in;
1450         mvertex_t       *out;
1451         int                     i, count;
1452
1453         in = (void *)(mod_base + l->fileofs);
1454         if (l->filelen % sizeof(*in))
1455                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1456         count = l->filelen / sizeof(*in);
1457         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1458
1459         loadmodel->brushq1.vertexes = out;
1460         loadmodel->brushq1.numvertexes = count;
1461
1462         for ( i=0 ; i<count ; i++, in++, out++)
1463         {
1464                 out->position[0] = LittleFloat(in->point[0]);
1465                 out->position[1] = LittleFloat(in->point[1]);
1466                 out->position[2] = LittleFloat(in->point[2]);
1467         }
1468 }
1469
1470 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1471 {
1472         dmodel_t        *in;
1473         dmodel_t        *out;
1474         int                     i, j, count;
1475
1476         in = (void *)(mod_base + l->fileofs);
1477         if (l->filelen % sizeof(*in))
1478                 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1479         count = l->filelen / sizeof(*in);
1480         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1481
1482         loadmodel->brushq1.submodels = out;
1483         loadmodel->brush.numsubmodels = count;
1484
1485         for ( i=0 ; i<count ; i++, in++, out++)
1486         {
1487                 for (j=0 ; j<3 ; j++)
1488                 {
1489                         // spread the mins / maxs by a pixel
1490                         out->mins[j] = LittleFloat(in->mins[j]) - 1;
1491                         out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1492                         out->origin[j] = LittleFloat(in->origin[j]);
1493                 }
1494                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1495                         out->headnode[j] = LittleLong(in->headnode[j]);
1496                 out->visleafs = LittleLong(in->visleafs);
1497                 out->firstface = LittleLong(in->firstface);
1498                 out->numfaces = LittleLong(in->numfaces);
1499         }
1500 }
1501
1502 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1503 {
1504         dedge_t *in;
1505         medge_t *out;
1506         int     i, count;
1507
1508         in = (void *)(mod_base + l->fileofs);
1509         if (l->filelen % sizeof(*in))
1510                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1511         count = l->filelen / sizeof(*in);
1512         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1513
1514         loadmodel->brushq1.edges = out;
1515         loadmodel->brushq1.numedges = count;
1516
1517         for ( i=0 ; i<count ; i++, in++, out++)
1518         {
1519                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1520                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1521         }
1522 }
1523
1524 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1525 {
1526         texinfo_t *in;
1527         mtexinfo_t *out;
1528         int i, j, k, count, miptex;
1529
1530         in = (void *)(mod_base + l->fileofs);
1531         if (l->filelen % sizeof(*in))
1532                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1533         count = l->filelen / sizeof(*in);
1534         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1535
1536         loadmodel->brushq1.texinfo = out;
1537         loadmodel->brushq1.numtexinfo = count;
1538
1539         for (i = 0;i < count;i++, in++, out++)
1540         {
1541                 for (k = 0;k < 2;k++)
1542                         for (j = 0;j < 4;j++)
1543                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1544
1545                 miptex = LittleLong(in->miptex);
1546                 out->flags = LittleLong(in->flags);
1547
1548                 out->texture = NULL;
1549                 if (loadmodel->brush.data_textures)
1550                 {
1551                         if ((unsigned int) miptex >= (unsigned int) loadmodel->brush.num_textures)
1552                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brush.num_textures);
1553                         else
1554                                 out->texture = loadmodel->brush.data_textures + miptex;
1555                 }
1556                 if (out->flags & TEX_SPECIAL)
1557                 {
1558                         // if texture chosen is NULL or the shader needs a lightmap,
1559                         // force to notexture water shader
1560                         if (out->texture == NULL || out->texture->flags & SURF_LIGHTMAP)
1561                                 out->texture = loadmodel->brush.data_textures + (loadmodel->brush.num_textures - 1);
1562                 }
1563                 else
1564                 {
1565                         // if texture chosen is NULL, force to notexture
1566                         if (out->texture == NULL)
1567                                 out->texture = loadmodel->brush.data_textures + (loadmodel->brush.num_textures - 2);
1568                 }
1569         }
1570 }
1571
1572 #if 0
1573 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1574 {
1575         int             i, j;
1576         float   *v;
1577
1578         mins[0] = mins[1] = mins[2] = 9999;
1579         maxs[0] = maxs[1] = maxs[2] = -9999;
1580         v = verts;
1581         for (i = 0;i < numverts;i++)
1582         {
1583                 for (j = 0;j < 3;j++, v++)
1584                 {
1585                         if (*v < mins[j])
1586                                 mins[j] = *v;
1587                         if (*v > maxs[j])
1588                                 maxs[j] = *v;
1589                 }
1590         }
1591 }
1592
1593 #define MAX_SUBDIVPOLYTRIANGLES 4096
1594 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1595
1596 static int subdivpolyverts, subdivpolytriangles;
1597 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1598 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1599
1600 static int subdivpolylookupvert(vec3_t v)
1601 {
1602         int i;
1603         for (i = 0;i < subdivpolyverts;i++)
1604                 if (subdivpolyvert[i][0] == v[0]
1605                  && subdivpolyvert[i][1] == v[1]
1606                  && subdivpolyvert[i][2] == v[2])
1607                         return i;
1608         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1609                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1610         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1611         return subdivpolyverts++;
1612 }
1613
1614 static void SubdividePolygon(int numverts, float *verts)
1615 {
1616         int             i, i1, i2, i3, f, b, c, p;
1617         vec3_t  mins, maxs, front[256], back[256];
1618         float   m, *pv, *cv, dist[256], frac;
1619
1620         if (numverts > 250)
1621                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1622
1623         BoundPoly(numverts, verts, mins, maxs);
1624
1625         for (i = 0;i < 3;i++)
1626         {
1627                 m = (mins[i] + maxs[i]) * 0.5;
1628                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1629                 if (maxs[i] - m < 8)
1630                         continue;
1631                 if (m - mins[i] < 8)
1632                         continue;
1633
1634                 // cut it
1635                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1636                         dist[c] = cv[i] - m;
1637
1638                 f = b = 0;
1639                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1640                 {
1641                         if (dist[p] >= 0)
1642                         {
1643                                 VectorCopy(pv, front[f]);
1644                                 f++;
1645                         }
1646                         if (dist[p] <= 0)
1647                         {
1648                                 VectorCopy(pv, back[b]);
1649                                 b++;
1650                         }
1651                         if (dist[p] == 0 || dist[c] == 0)
1652                                 continue;
1653                         if ((dist[p] > 0) != (dist[c] > 0) )
1654                         {
1655                                 // clip point
1656                                 frac = dist[p] / (dist[p] - dist[c]);
1657                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1658                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1659                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1660                                 f++;
1661                                 b++;
1662                         }
1663                 }
1664
1665                 SubdividePolygon(f, front[0]);
1666                 SubdividePolygon(b, back[0]);
1667                 return;
1668         }
1669
1670         i1 = subdivpolylookupvert(verts);
1671         i2 = subdivpolylookupvert(verts + 3);
1672         for (i = 2;i < numverts;i++)
1673         {
1674                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1675                 {
1676                         Con_Print("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1677                         return;
1678                 }
1679
1680                 i3 = subdivpolylookupvert(verts + i * 3);
1681                 subdivpolyindex[subdivpolytriangles][0] = i1;
1682                 subdivpolyindex[subdivpolytriangles][1] = i2;
1683                 subdivpolyindex[subdivpolytriangles][2] = i3;
1684                 i2 = i3;
1685                 subdivpolytriangles++;
1686         }
1687 }
1688
1689 //Breaks a polygon up along axial 64 unit
1690 //boundaries so that turbulent and sky warps
1691 //can be done reasonably.
1692 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surface)
1693 {
1694         int i, j;
1695         surfvertex_t *v;
1696         surfmesh_t *mesh;
1697
1698         subdivpolytriangles = 0;
1699         subdivpolyverts = 0;
1700         SubdividePolygon(surface->mesh.num_vertices, surface->mesh.data_vertex3f);
1701         if (subdivpolytriangles < 1)
1702                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1703
1704         surface->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1705         mesh->num_vertices = subdivpolyverts;
1706         mesh->num_triangles = subdivpolytriangles;
1707         mesh->vertex = (surfvertex_t *)(mesh + 1);
1708         mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1709         memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1710
1711         for (i = 0;i < mesh->num_triangles;i++)
1712                 for (j = 0;j < 3;j++)
1713                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1714
1715         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1716         {
1717                 VectorCopy(subdivpolyvert[i], v->v);
1718                 v->st[0] = DotProduct(v->v, surface->texinfo->vecs[0]);
1719                 v->st[1] = DotProduct(v->v, surface->texinfo->vecs[1]);
1720         }
1721 }
1722 #endif
1723
1724 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1725 {
1726         dface_t *in;
1727         msurface_t *surface;
1728         int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris;
1729         float texmins[2], texmaxs[2], val;
1730
1731         in = (void *)(mod_base + l->fileofs);
1732         if (l->filelen % sizeof(*in))
1733                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1734         count = l->filelen / sizeof(*in);
1735         loadmodel->brush.data_surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1736
1737         loadmodel->brush.num_surfaces = count;
1738
1739         totalverts = 0;
1740         totaltris = 0;
1741         for (surfacenum = 0, in = (void *)(mod_base + l->fileofs);surfacenum < count;surfacenum++, in++)
1742         {
1743                 numedges = LittleShort(in->numedges);
1744                 totalverts += numedges;
1745                 totaltris += numedges - 2;
1746         }
1747
1748         // TODO: split up into multiple meshes as needed to avoid exceeding 65536
1749         // vertex limit
1750         loadmodel->nummeshes = 1;
1751         loadmodel->meshlist = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t *));
1752         loadmodel->meshlist[0] = Mod_AllocSurfMesh(loadmodel->mempool, totalverts, totaltris, 0, 0, true, true, false);
1753
1754         totalverts = 0;
1755         totaltris = 0;
1756         for (surfacenum = 0, in = (void *)(mod_base + l->fileofs), surface = loadmodel->brush.data_surfaces;surfacenum < count;surfacenum++, in++, surface++)
1757         {
1758                 // FIXME: validate edges, texinfo, etc?
1759                 firstedge = LittleLong(in->firstedge);
1760                 numedges = LittleShort(in->numedges);
1761                 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)
1762                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1763                 i = LittleShort(in->texinfo);
1764                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1765                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1766                 surface->texinfo = loadmodel->brushq1.texinfo + i;
1767                 surface->texture = surface->texinfo->texture;
1768                 surface->flags = surface->texture->flags;
1769
1770                 planenum = LittleShort(in->planenum);
1771                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brush.num_planes)
1772                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brush.num_planes);
1773
1774                 if (LittleShort(in->side))
1775                         surface->flags |= SURF_PLANEBACK;
1776
1777                 surface->plane = loadmodel->brush.data_planes + planenum;
1778
1779                 surface->mesh.num_vertices = numedges;
1780                 surface->mesh.num_triangles = numedges - 2;
1781                 surface->mesh.data_vertex3f = loadmodel->meshlist[0]->data_vertex3f + totalverts * 3;
1782                 surface->mesh.data_texcoordtexture2f = loadmodel->meshlist[0]->data_texcoordtexture2f + totalverts * 2;
1783                 surface->mesh.data_texcoordlightmap2f = loadmodel->meshlist[0]->data_texcoordlightmap2f + totalverts * 2;
1784                 surface->mesh.data_texcoorddetail2f = loadmodel->meshlist[0]->data_texcoorddetail2f + totalverts * 2;
1785                 surface->mesh.data_svector3f = loadmodel->meshlist[0]->data_svector3f + totalverts * 3;
1786                 surface->mesh.data_tvector3f = loadmodel->meshlist[0]->data_tvector3f + totalverts * 3;
1787                 surface->mesh.data_normal3f = loadmodel->meshlist[0]->data_normal3f + totalverts * 3;
1788                 surface->mesh.data_lightmapoffsets = loadmodel->meshlist[0]->data_lightmapoffsets + totalverts;
1789                 surface->mesh.data_element3i = loadmodel->meshlist[0]->data_element3i + totaltris * 3;
1790                 surface->mesh.data_neighbor3i = loadmodel->meshlist[0]->data_neighbor3i + totaltris * 3;
1791                 totalverts += numedges;
1792                 totaltris += numedges - 2;
1793
1794                 // convert edges back to a normal polygon
1795                 for (i = 0;i < surface->mesh.num_vertices;i++)
1796                 {
1797                         int lindex = loadmodel->brushq1.surfedges[firstedge + i];
1798                         float s, t;
1799                         if (lindex > 0)
1800                                 VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position, surface->mesh.data_vertex3f + i * 3);
1801                         else
1802                                 VectorCopy(loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position, surface->mesh.data_vertex3f + i * 3);
1803                         s = DotProduct((surface->mesh.data_vertex3f + i * 3), surface->texinfo->vecs[0]) + surface->texinfo->vecs[0][3];
1804                         t = DotProduct((surface->mesh.data_vertex3f + i * 3), surface->texinfo->vecs[1]) + surface->texinfo->vecs[1][3];
1805                         surface->mesh.data_texcoordtexture2f[i * 2 + 0] = s / surface->texture->width;
1806                         surface->mesh.data_texcoordtexture2f[i * 2 + 1] = t / surface->texture->height;
1807                         surface->mesh.data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1808                         surface->mesh.data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1809                         surface->mesh.data_texcoordlightmap2f[i * 2 + 0] = 0;
1810                         surface->mesh.data_texcoordlightmap2f[i * 2 + 1] = 0;
1811                         surface->mesh.data_lightmapoffsets[i] = 0;
1812                 }
1813
1814                 for (i = 0;i < surface->mesh.num_triangles;i++)
1815                 {
1816                         surface->mesh.data_element3i[i * 3 + 0] = 0;
1817                         surface->mesh.data_element3i[i * 3 + 1] = i + 1;
1818                         surface->mesh.data_element3i[i * 3 + 2] = i + 2;
1819                 }
1820
1821                 // compile additional data about the surface geometry
1822                 Mod_BuildTriangleNeighbors(surface->mesh.data_neighbor3i, surface->mesh.data_element3i, surface->mesh.num_triangles);
1823                 Mod_BuildTextureVectorsAndNormals(surface->mesh.num_vertices, surface->mesh.num_triangles, surface->mesh.data_vertex3f, surface->mesh.data_texcoordtexture2f, surface->mesh.data_element3i, surface->mesh.data_svector3f, surface->mesh.data_tvector3f, surface->mesh.data_normal3f);
1824                 BoxFromPoints(surface->mins, surface->maxs, surface->mesh.num_vertices, surface->mesh.data_vertex3f);
1825
1826                 // generate surface extents information
1827                 texmins[0] = texmaxs[0] = DotProduct(surface->mesh.data_vertex3f, surface->texinfo->vecs[0]) + surface->texinfo->vecs[0][3];
1828                 texmins[1] = texmaxs[1] = DotProduct(surface->mesh.data_vertex3f, surface->texinfo->vecs[1]) + surface->texinfo->vecs[1][3];
1829                 for (i = 1;i < surface->mesh.num_vertices;i++)
1830                 {
1831                         for (j = 0;j < 2;j++)
1832                         {
1833                                 val = DotProduct(surface->mesh.data_vertex3f + i * 3, surface->texinfo->vecs[j]) + surface->texinfo->vecs[j][3];
1834                                 texmins[j] = min(texmins[j], val);
1835                                 texmaxs[j] = max(texmaxs[j], val);
1836                         }
1837                 }
1838                 for (i = 0;i < 2;i++)
1839                 {
1840                         surface->texturemins[i] = (int) floor(texmins[i] / 16.0) * 16;
1841                         surface->extents[i] = (int) ceil(texmaxs[i] / 16.0) * 16 - surface->texturemins[i];
1842                 }
1843
1844                 smax = surface->extents[0] >> 4;
1845                 tmax = surface->extents[1] >> 4;
1846                 ssize = (surface->extents[0] >> 4) + 1;
1847                 tsize = (surface->extents[1] >> 4) + 1;
1848
1849                 // lighting info
1850                 for (i = 0;i < MAXLIGHTMAPS;i++)
1851                         surface->styles[i] = in->styles[i];
1852                 // force lightmap upload on first time seeing the surface
1853                 surface->cached_dlight = true;
1854                 surface->lightmaptexturestride = 0;
1855                 surface->lightmaptexture = NULL;
1856                 i = LittleLong(in->lightofs);
1857                 if (i == -1)
1858                         surface->samples = NULL;
1859                 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1860                         surface->samples = loadmodel->brushq1.lightdata + i;
1861                 else // LordHavoc: white lighting (bsp version 29)
1862                         surface->samples = loadmodel->brushq1.lightdata + (i * 3);
1863
1864                 if (surface->texture->flags & SURF_LIGHTMAP)
1865                 {
1866                         if (ssize > 256 || tsize > 256)
1867                                 Host_Error("Bad surface extents");
1868                         // stainmap for permanent marks on walls
1869                         surface->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1870                         // clear to white
1871                         memset(surface->stainsamples, 255, ssize * tsize * 3);
1872                 }
1873
1874                 if (surface->texture->flags & SURF_LIGHTMAP)
1875                 {
1876                         int i, iu, iv;
1877                         float u, v, ubase, vbase, uscale, vscale;
1878
1879                         if (r_miplightmaps.integer)
1880                         {
1881                                 surface->lightmaptexturestride = ssize;
1882                                 surface->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surface->lightmaptexturestride, tsize, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1883                         }
1884                         else
1885                         {
1886                                 surface->lightmaptexturestride = R_CompatibleFragmentWidth(ssize, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1887                                 surface->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surface->lightmaptexturestride, tsize, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1888                         }
1889                         R_FragmentLocation(surface->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1890                         uscale = (uscale - ubase) / ssize;
1891                         vscale = (vscale - vbase) / tsize;
1892
1893                         for (i = 0;i < surface->mesh.num_vertices;i++)
1894                         {
1895                                 u = ((DotProduct((surface->mesh.data_vertex3f + i * 3), surface->texinfo->vecs[0]) + surface->texinfo->vecs[0][3]) + 8 - surface->texturemins[0]) * (1.0 / 16.0);
1896                                 v = ((DotProduct((surface->mesh.data_vertex3f + i * 3), surface->texinfo->vecs[1]) + surface->texinfo->vecs[1][3]) + 8 - surface->texturemins[1]) * (1.0 / 16.0);
1897                                 surface->mesh.data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1898                                 surface->mesh.data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1899                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1900                                 iu = (int) u;
1901                                 iv = (int) v;
1902                                 surface->mesh.data_lightmapoffsets[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
1903                         }
1904                 }
1905         }
1906 }
1907
1908 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1909 {
1910         node->parent = parent;
1911         if (node->plane)
1912         {
1913                 Mod_Q1BSP_SetParent(node->children[0], node);
1914                 Mod_Q1BSP_SetParent(node->children[1], node);
1915         }
1916 }
1917
1918 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1919 {
1920         int                     i, j, count, p;
1921         dnode_t         *in;
1922         mnode_t         *out;
1923
1924         in = (void *)(mod_base + l->fileofs);
1925         if (l->filelen % sizeof(*in))
1926                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1927         count = l->filelen / sizeof(*in);
1928         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1929
1930         loadmodel->brush.data_nodes = out;
1931         loadmodel->brush.num_nodes = count;
1932
1933         for ( i=0 ; i<count ; i++, in++, out++)
1934         {
1935                 for (j=0 ; j<3 ; j++)
1936                 {
1937                         out->mins[j] = LittleShort(in->mins[j]);
1938                         out->maxs[j] = LittleShort(in->maxs[j]);
1939                 }
1940
1941                 p = LittleLong(in->planenum);
1942                 out->plane = loadmodel->brush.data_planes + p;
1943
1944                 out->firstsurface = LittleShort(in->firstface);
1945                 out->numsurfaces = LittleShort(in->numfaces);
1946
1947                 for (j=0 ; j<2 ; j++)
1948                 {
1949                         p = LittleShort(in->children[j]);
1950                         if (p >= 0)
1951                                 out->children[j] = loadmodel->brush.data_nodes + p;
1952                         else
1953                                 out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + (-1 - p));
1954                 }
1955         }
1956
1957         Mod_Q1BSP_SetParent(loadmodel->brush.data_nodes, NULL); // sets nodes and leafs
1958 }
1959
1960 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1961 {
1962         dleaf_t *in;
1963         mleaf_t *out;
1964         int i, j, count, p;
1965
1966         in = (void *)(mod_base + l->fileofs);
1967         if (l->filelen % sizeof(*in))
1968                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1969         count = l->filelen / sizeof(*in);
1970         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1971
1972         loadmodel->brush.data_leafs = out;
1973         loadmodel->brush.num_leafs = count;
1974         // get visleafs from the submodel data
1975         loadmodel->brush.num_pvsclusters = loadmodel->brushq1.submodels[0].visleafs;
1976         loadmodel->brush.num_pvsclusterbytes = (loadmodel->brush.num_pvsclusters+7)>>3;
1977         loadmodel->brush.data_pvsclusters = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
1978         memset(loadmodel->brush.data_pvsclusters, 0xFF, loadmodel->brush.num_pvsclusters * loadmodel->brush.num_pvsclusterbytes);
1979
1980         for ( i=0 ; i<count ; i++, in++, out++)
1981         {
1982                 for (j=0 ; j<3 ; j++)
1983                 {
1984                         out->mins[j] = LittleShort(in->mins[j]);
1985                         out->maxs[j] = LittleShort(in->maxs[j]);
1986                 }
1987
1988                 // FIXME: this function could really benefit from some error checking
1989
1990                 out->contents = LittleLong(in->contents);
1991
1992                 out->firstleafsurface = loadmodel->brush.data_leafsurfaces + LittleShort(in->firstmarksurface);
1993                 out->numleafsurfaces = LittleShort(in->nummarksurfaces);
1994                 if (out->firstleafsurface < 0 || LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
1995                 {
1996                         Con_Printf("Mod_Q1BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", out->firstleafsurface, out->firstleafsurface + out->numleafsurfaces, 0, loadmodel->brush.num_leafsurfaces);
1997                         out->firstleafsurface = NULL;
1998                         out->numleafsurfaces = 0;
1999                 }
2000
2001                 out->clusterindex = i - 1;
2002                 if (out->clusterindex >= loadmodel->brush.num_pvsclusters)
2003                         out->clusterindex = -1;
2004
2005                 p = LittleLong(in->visofs);
2006                 // ignore visofs errors on leaf 0 (solid)
2007                 if (p >= 0 && out->clusterindex >= 0)
2008                 {
2009                         if (p >= loadmodel->brushq1.num_compressedpvs)
2010                                 Con_Print("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
2011                         else
2012                                 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);
2013                 }
2014
2015                 for (j = 0;j < 4;j++)
2016                         out->ambient_sound_level[j] = in->ambient_level[j];
2017
2018                 // FIXME: Insert caustics here
2019         }
2020 }
2021
2022 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
2023 {
2024         dclipnode_t *in, *out;
2025         int                     i, count;
2026         hull_t          *hull;
2027
2028         in = (void *)(mod_base + l->fileofs);
2029         if (l->filelen % sizeof(*in))
2030                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
2031         count = l->filelen / sizeof(*in);
2032         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2033
2034         loadmodel->brushq1.clipnodes = out;
2035         loadmodel->brushq1.numclipnodes = count;
2036
2037         if (loadmodel->brush.ishlbsp)
2038         {
2039                 hull = &loadmodel->brushq1.hulls[1];
2040                 hull->clipnodes = out;
2041                 hull->firstclipnode = 0;
2042                 hull->lastclipnode = count-1;
2043                 hull->planes = loadmodel->brush.data_planes;
2044                 hull->clip_mins[0] = -16;
2045                 hull->clip_mins[1] = -16;
2046                 hull->clip_mins[2] = -36;
2047                 hull->clip_maxs[0] = 16;
2048                 hull->clip_maxs[1] = 16;
2049                 hull->clip_maxs[2] = 36;
2050                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2051
2052                 hull = &loadmodel->brushq1.hulls[2];
2053                 hull->clipnodes = out;
2054                 hull->firstclipnode = 0;
2055                 hull->lastclipnode = count-1;
2056                 hull->planes = loadmodel->brush.data_planes;
2057                 hull->clip_mins[0] = -32;
2058                 hull->clip_mins[1] = -32;
2059                 hull->clip_mins[2] = -32;
2060                 hull->clip_maxs[0] = 32;
2061                 hull->clip_maxs[1] = 32;
2062                 hull->clip_maxs[2] = 32;
2063                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2064
2065                 hull = &loadmodel->brushq1.hulls[3];
2066                 hull->clipnodes = out;
2067                 hull->firstclipnode = 0;
2068                 hull->lastclipnode = count-1;
2069                 hull->planes = loadmodel->brush.data_planes;
2070                 hull->clip_mins[0] = -16;
2071                 hull->clip_mins[1] = -16;
2072                 hull->clip_mins[2] = -18;
2073                 hull->clip_maxs[0] = 16;
2074                 hull->clip_maxs[1] = 16;
2075                 hull->clip_maxs[2] = 18;
2076                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2077         }
2078         else
2079         {
2080                 hull = &loadmodel->brushq1.hulls[1];
2081                 hull->clipnodes = out;
2082                 hull->firstclipnode = 0;
2083                 hull->lastclipnode = count-1;
2084                 hull->planes = loadmodel->brush.data_planes;
2085                 hull->clip_mins[0] = -16;
2086                 hull->clip_mins[1] = -16;
2087                 hull->clip_mins[2] = -24;
2088                 hull->clip_maxs[0] = 16;
2089                 hull->clip_maxs[1] = 16;
2090                 hull->clip_maxs[2] = 32;
2091                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2092
2093                 hull = &loadmodel->brushq1.hulls[2];
2094                 hull->clipnodes = out;
2095                 hull->firstclipnode = 0;
2096                 hull->lastclipnode = count-1;
2097                 hull->planes = loadmodel->brush.data_planes;
2098                 hull->clip_mins[0] = -32;
2099                 hull->clip_mins[1] = -32;
2100                 hull->clip_mins[2] = -24;
2101                 hull->clip_maxs[0] = 32;
2102                 hull->clip_maxs[1] = 32;
2103                 hull->clip_maxs[2] = 64;
2104                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2105         }
2106
2107         for (i=0 ; i<count ; i++, out++, in++)
2108         {
2109                 out->planenum = LittleLong(in->planenum);
2110                 out->children[0] = LittleShort(in->children[0]);
2111                 out->children[1] = LittleShort(in->children[1]);
2112                 if (out->children[0] >= count || out->children[1] >= count)
2113                         Host_Error("Corrupt clipping hull(out of range child)\n");
2114         }
2115 }
2116
2117 //Duplicate the drawing hull structure as a clipping hull
2118 static void Mod_Q1BSP_MakeHull0(void)
2119 {
2120         mnode_t         *in;
2121         dclipnode_t *out;
2122         int                     i;
2123         hull_t          *hull;
2124
2125         hull = &loadmodel->brushq1.hulls[0];
2126
2127         in = loadmodel->brush.data_nodes;
2128         out = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_nodes * sizeof(dclipnode_t));
2129
2130         hull->clipnodes = out;
2131         hull->firstclipnode = 0;
2132         hull->lastclipnode = loadmodel->brush.num_nodes - 1;
2133         hull->planes = loadmodel->brush.data_planes;
2134
2135         for (i = 0;i < loadmodel->brush.num_nodes;i++, out++, in++)
2136         {
2137                 out->planenum = in->plane - loadmodel->brush.data_planes;
2138                 out->children[0] = in->children[0]->plane ? in->children[0] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[0])->contents;
2139                 out->children[1] = in->children[1]->plane ? in->children[1] - loadmodel->brush.data_nodes : ((mleaf_t *)in->children[1])->contents;
2140         }
2141 }
2142
2143 static void Mod_Q1BSP_LoadLeaffaces(lump_t *l)
2144 {
2145         int i, j;
2146         short *in;
2147
2148         in = (void *)(mod_base + l->fileofs);
2149         if (l->filelen % sizeof(*in))
2150                 Host_Error("Mod_Q1BSP_LoadLeaffaces: funny lump size in %s",loadmodel->name);
2151         loadmodel->brush.num_leafsurfaces = l->filelen / sizeof(*in);
2152         loadmodel->brush.data_leafsurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_leafsurfaces * sizeof(int));
2153
2154         for (i = 0;i < loadmodel->brush.num_leafsurfaces;i++)
2155         {
2156                 j = (unsigned) LittleShort(in[i]);
2157                 if (j >= loadmodel->brush.num_surfaces)
2158                         Host_Error("Mod_Q1BSP_LoadLeaffaces: bad surface number");
2159                 loadmodel->brush.data_leafsurfaces[i] = j;
2160         }
2161 }
2162
2163 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2164 {
2165         int             i;
2166         int             *in;
2167
2168         in = (void *)(mod_base + l->fileofs);
2169         if (l->filelen % sizeof(*in))
2170                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2171         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2172         loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2173
2174         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2175                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2176 }
2177
2178
2179 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2180 {
2181         int                     i;
2182         mplane_t        *out;
2183         dplane_t        *in;
2184
2185         in = (void *)(mod_base + l->fileofs);
2186         if (l->filelen % sizeof(*in))
2187                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2188
2189         loadmodel->brush.num_planes = l->filelen / sizeof(*in);
2190         loadmodel->brush.data_planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_planes * sizeof(*out));
2191
2192         for (i = 0;i < loadmodel->brush.num_planes;i++, in++, out++)
2193         {
2194                 out->normal[0] = LittleFloat(in->normal[0]);
2195                 out->normal[1] = LittleFloat(in->normal[1]);
2196                 out->normal[2] = LittleFloat(in->normal[2]);
2197                 out->dist = LittleFloat(in->dist);
2198
2199                 PlaneClassify(out);
2200         }
2201 }
2202
2203 static void Mod_Q1BSP_LoadMapBrushes(void)
2204 {
2205 #if 0
2206 // unfinished
2207         int submodel, numbrushes;
2208         qboolean firstbrush;
2209         char *text, *maptext;
2210         char mapfilename[MAX_QPATH];
2211         FS_StripExtension (loadmodel->name, mapfilename, sizeof (mapfilename));
2212         strlcat (mapfilename, ".map", sizeof (mapfilename));
2213         maptext = (qbyte*) FS_LoadFile(mapfilename, tempmempool, false);
2214         if (!maptext)
2215                 return;
2216         text = maptext;
2217         if (!COM_ParseToken(&data, false))
2218                 return; // error
2219         submodel = 0;
2220         for (;;)
2221         {
2222                 if (!COM_ParseToken(&data, false))
2223                         break;
2224                 if (com_token[0] != '{')
2225                         return; // error
2226                 // entity
2227                 firstbrush = true;
2228                 numbrushes = 0;
2229                 maxbrushes = 256;
2230                 brushes = Mem_Alloc(loadmodel->mempool, maxbrushes * sizeof(mbrush_t));
2231                 for (;;)
2232                 {
2233                         if (!COM_ParseToken(&data, false))
2234                                 return; // error
2235                         if (com_token[0] == '}')
2236                                 break; // end of entity
2237                         if (com_token[0] == '{')
2238                         {
2239                                 // brush
2240                                 if (firstbrush)
2241                                 {
2242                                         if (submodel)
2243                                         {
2244                                                 if (submodel > loadmodel->brush.numsubmodels)
2245                                                 {
2246                                                         Con_Printf("Mod_Q1BSP_LoadMapBrushes: .map has more submodels than .bsp!\n");
2247                                                         model = NULL;
2248                                                 }
2249                                                 else
2250                                                         model = loadmodel->brush.submodels[submodel];
2251                                         }
2252                                         else
2253                                                 model = loadmodel;
2254                                 }
2255                                 for (;;)
2256                                 {
2257                                         if (!COM_ParseToken(&data, false))
2258                                                 return; // error
2259                                         if (com_token[0] == '}')
2260                                                 break; // end of brush
2261                                         // each brush face should be this format:
2262                                         // ( x y z ) ( x y z ) ( x y z ) texture scroll_s scroll_t rotateangle scale_s scale_t
2263                                         // FIXME: support hl .map format
2264                                         for (pointnum = 0;pointnum < 3;pointnum++)
2265                                         {
2266                                                 COM_ParseToken(&data, false);
2267                                                 for (componentnum = 0;componentnum < 3;componentnum++)
2268                                                 {
2269                                                         COM_ParseToken(&data, false);
2270                                                         point[pointnum][componentnum] = atof(com_token);
2271                                                 }
2272                                                 COM_ParseToken(&data, false);
2273                                         }
2274                                         COM_ParseToken(&data, false);
2275                                         strlcpy(facetexture, com_token, sizeof(facetexture));
2276                                         COM_ParseToken(&data, false);
2277                                         //scroll_s = atof(com_token);
2278                                         COM_ParseToken(&data, false);
2279                                         //scroll_t = atof(com_token);
2280                                         COM_ParseToken(&data, false);
2281                                         //rotate = atof(com_token);
2282                                         COM_ParseToken(&data, false);
2283                                         //scale_s = atof(com_token);
2284                                         COM_ParseToken(&data, false);
2285                                         //scale_t = atof(com_token);
2286                                         TriangleNormal(point[0], point[1], point[2], planenormal);
2287                                         VectorNormalizeDouble(planenormal);
2288                                         planedist = DotProduct(point[0], planenormal);
2289                                         //ChooseTexturePlane(planenormal, texturevector[0], texturevector[1]);
2290                                 }
2291                                 continue;
2292                         }
2293                 }
2294         }
2295 #endif
2296 }
2297
2298
2299 #define MAX_PORTALPOINTS 64
2300
2301 typedef struct portal_s
2302 {
2303         mplane_t plane;
2304         mnode_t *nodes[2];              // [0] = front side of plane
2305         struct portal_s *next[2];
2306         int numpoints;
2307         double points[3*MAX_PORTALPOINTS];
2308         struct portal_s *chain; // all portals are linked into a list
2309 }
2310 portal_t;
2311
2312 static portal_t *portalchain;
2313
2314 /*
2315 ===========
2316 AllocPortal
2317 ===========
2318 */
2319 static portal_t *AllocPortal(void)
2320 {
2321         portal_t *p;
2322         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2323         p->chain = portalchain;
2324         portalchain = p;
2325         return p;
2326 }
2327
2328 static void FreePortal(portal_t *p)
2329 {
2330         Mem_Free(p);
2331 }
2332
2333 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2334 {
2335         // process only nodes (leafs already had their box calculated)
2336         if (!node->plane)
2337                 return;
2338
2339         // calculate children first
2340         Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2341         Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2342
2343         // make combined bounding box from children
2344         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2345         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2346         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2347         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2348         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2349         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2350 }
2351
2352 static void Mod_Q1BSP_FinalizePortals(void)
2353 {
2354         int i, j, numportals, numpoints;
2355         portal_t *p, *pnext;
2356         mportal_t *portal;
2357         mvertex_t *point;
2358         mleaf_t *leaf, *endleaf;
2359
2360         // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2361         leaf = loadmodel->brush.data_leafs;
2362         endleaf = leaf + loadmodel->brush.num_leafs;
2363         for (;leaf < endleaf;leaf++)
2364         {
2365                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2366                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2367         }
2368         p = portalchain;
2369         while (p)
2370         {
2371                 if (p->numpoints >= 3)
2372                 {
2373                         for (i = 0;i < 2;i++)
2374                         {
2375                                 leaf = (mleaf_t *)p->nodes[i];
2376                                 for (j = 0;j < p->numpoints;j++)
2377                                 {
2378                                         if (leaf->mins[0] > p->points[j*3+0]) leaf->mins[0] = p->points[j*3+0];
2379                                         if (leaf->mins[1] > p->points[j*3+1]) leaf->mins[1] = p->points[j*3+1];
2380                                         if (leaf->mins[2] > p->points[j*3+2]) leaf->mins[2] = p->points[j*3+2];
2381                                         if (leaf->maxs[0] < p->points[j*3+0]) leaf->maxs[0] = p->points[j*3+0];
2382                                         if (leaf->maxs[1] < p->points[j*3+1]) leaf->maxs[1] = p->points[j*3+1];
2383                                         if (leaf->maxs[2] < p->points[j*3+2]) leaf->maxs[2] = p->points[j*3+2];
2384                                 }
2385                         }
2386                 }
2387                 p = p->chain;
2388         }
2389
2390         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brush.data_nodes);
2391
2392         // tally up portal and point counts
2393         p = portalchain;
2394         numportals = 0;
2395         numpoints = 0;
2396         while (p)
2397         {
2398                 // note: this check must match the one below or it will usually corrupt memory
2399                 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2400                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1] && ((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
2401                 {
2402                         numportals += 2;
2403                         numpoints += p->numpoints * 2;
2404                 }
2405                 p = p->chain;
2406         }
2407         loadmodel->brush.data_portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2408         loadmodel->brush.num_portals = numportals;
2409         loadmodel->brush.data_portalpoints = (void *)((qbyte *) loadmodel->brush.data_portals + numportals * sizeof(mportal_t));
2410         loadmodel->brush.num_portalpoints = numpoints;
2411         // clear all leaf portal chains
2412         for (i = 0;i < loadmodel->brush.num_leafs;i++)
2413                 loadmodel->brush.data_leafs[i].portals = NULL;
2414         // process all portals in the global portal chain, while freeing them
2415         portal = loadmodel->brush.data_portals;
2416         point = loadmodel->brush.data_portalpoints;
2417         p = portalchain;
2418         portalchain = NULL;
2419         while (p)
2420         {
2421                 pnext = p->chain;
2422
2423                 // note: this check must match the one above or it will usually corrupt memory
2424                 // 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
2425                 if (p->numpoints >= 3 && p->nodes[0] != p->nodes[1] && ((mleaf_t *)p->nodes[0])->clusterindex >= 0 && ((mleaf_t *)p->nodes[1])->clusterindex >= 0)
2426                 {
2427                         // first make the back to front portal(forward portal)
2428                         portal->points = point;
2429                         portal->numpoints = p->numpoints;
2430                         portal->plane.dist = p->plane.dist;
2431                         VectorCopy(p->plane.normal, portal->plane.normal);
2432                         portal->here = (mleaf_t *)p->nodes[1];
2433                         portal->past = (mleaf_t *)p->nodes[0];
2434                         // copy points
2435                         for (j = 0;j < portal->numpoints;j++)
2436                         {
2437                                 VectorCopy(p->points + j*3, point->position);
2438                                 point++;
2439                         }
2440                         BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
2441                         PlaneClassify(&portal->plane);
2442
2443                         // link into leaf's portal chain
2444                         portal->next = portal->here->portals;
2445                         portal->here->portals = portal;
2446
2447                         // advance to next portal
2448                         portal++;
2449
2450                         // then make the front to back portal(backward portal)
2451                         portal->points = point;
2452                         portal->numpoints = p->numpoints;
2453                         portal->plane.dist = -p->plane.dist;
2454                         VectorNegate(p->plane.normal, portal->plane.normal);
2455                         portal->here = (mleaf_t *)p->nodes[0];
2456                         portal->past = (mleaf_t *)p->nodes[1];
2457                         // copy points
2458                         for (j = portal->numpoints - 1;j >= 0;j--)
2459                         {
2460                                 VectorCopy(p->points + j*3, point->position);
2461                                 point++;
2462                         }
2463                         BoxFromPoints(portal->mins, portal->maxs, portal->numpoints, portal->points->position);
2464                         PlaneClassify(&portal->plane);
2465
2466                         // link into leaf's portal chain
2467                         portal->next = portal->here->portals;
2468                         portal->here->portals = portal;
2469
2470                         // advance to next portal
2471                         portal++;
2472                 }
2473                 FreePortal(p);
2474                 p = pnext;
2475         }
2476 }
2477
2478 /*
2479 =============
2480 AddPortalToNodes
2481 =============
2482 */
2483 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2484 {
2485         if (!front)
2486                 Host_Error("AddPortalToNodes: NULL front node");
2487         if (!back)
2488                 Host_Error("AddPortalToNodes: NULL back node");
2489         if (p->nodes[0] || p->nodes[1])
2490                 Host_Error("AddPortalToNodes: already included");
2491         // 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
2492
2493         p->nodes[0] = front;
2494         p->next[0] = (portal_t *)front->portals;
2495         front->portals = (mportal_t *)p;
2496
2497         p->nodes[1] = back;
2498         p->next[1] = (portal_t *)back->portals;
2499         back->portals = (mportal_t *)p;
2500 }
2501
2502 /*
2503 =============
2504 RemovePortalFromNode
2505 =============
2506 */
2507 static void RemovePortalFromNodes(portal_t *portal)
2508 {
2509         int i;
2510         mnode_t *node;
2511         void **portalpointer;
2512         portal_t *t;
2513         for (i = 0;i < 2;i++)
2514         {
2515                 node = portal->nodes[i];
2516
2517                 portalpointer = (void **) &node->portals;
2518                 while (1)
2519                 {
2520                         t = *portalpointer;
2521                         if (!t)
2522                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2523
2524                         if (t == portal)
2525                         {
2526                                 if (portal->nodes[0] == node)
2527                                 {
2528                                         *portalpointer = portal->next[0];
2529                                         portal->nodes[0] = NULL;
2530                                 }
2531                                 else if (portal->nodes[1] == node)
2532                                 {
2533                                         *portalpointer = portal->next[1];
2534                                         portal->nodes[1] = NULL;
2535                                 }
2536                                 else
2537                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2538                                 break;
2539                         }
2540
2541                         if (t->nodes[0] == node)
2542                                 portalpointer = (void **) &t->next[0];
2543                         else if (t->nodes[1] == node)
2544                                 portalpointer = (void **) &t->next[1];
2545                         else
2546                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2547                 }
2548         }
2549 }
2550
2551 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2552 {
2553         int i, side;
2554         mnode_t *front, *back, *other_node;
2555         mplane_t clipplane, *plane;
2556         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2557         int numfrontpoints, numbackpoints;
2558         double frontpoints[3*MAX_PORTALPOINTS], backpoints[3*MAX_PORTALPOINTS];
2559
2560         // if a leaf, we're done
2561         if (!node->plane)
2562                 return;
2563
2564         plane = node->plane;
2565
2566         front = node->children[0];
2567         back = node->children[1];
2568         if (front == back)
2569                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2570
2571         // create the new portal by generating a polygon for the node plane,
2572         // and clipping it by all of the other portals(which came from nodes above this one)
2573         nodeportal = AllocPortal();
2574         nodeportal->plane = *plane;
2575
2576         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);
2577         nodeportal->numpoints = 4;
2578         side = 0;       // shut up compiler warning
2579         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2580         {
2581                 clipplane = portal->plane;
2582                 if (portal->nodes[0] == portal->nodes[1])
2583                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2584                 if (portal->nodes[0] == node)
2585                         side = 0;
2586                 else if (portal->nodes[1] == node)
2587                 {
2588                         clipplane.dist = -clipplane.dist;
2589                         VectorNegate(clipplane.normal, clipplane.normal);
2590                         side = 1;
2591                 }
2592                 else
2593                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2594
2595                 for (i = 0;i < nodeportal->numpoints*3;i++)
2596                         frontpoints[i] = nodeportal->points[i];
2597                 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);
2598                 if (nodeportal->numpoints <= 0 || nodeportal->numpoints >= MAX_PORTALPOINTS)
2599                         break;
2600         }
2601
2602         if (nodeportal->numpoints < 3)
2603         {
2604                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2605                 nodeportal->numpoints = 0;
2606         }
2607         else if (nodeportal->numpoints >= MAX_PORTALPOINTS)
2608         {
2609                 Con_Print("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal has too many points\n");
2610                 nodeportal->numpoints = 0;
2611         }
2612         else
2613         {
2614                 AddPortalToNodes(nodeportal, front, back);
2615
2616                 // split the portals of this node along this node's plane and assign them to the children of this node
2617                 // (migrating the portals downward through the tree)
2618                 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2619                 {
2620                         if (portal->nodes[0] == portal->nodes[1])
2621                                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2622                         if (portal->nodes[0] == node)
2623                                 side = 0;
2624                         else if (portal->nodes[1] == node)
2625                                 side = 1;
2626                         else
2627                                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2628                         nextportal = portal->next[side];
2629
2630                         other_node = portal->nodes[!side];
2631                         RemovePortalFromNodes(portal);
2632
2633                         // cut the portal into two portals, one on each side of the node plane
2634                         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);
2635
2636                         if (!numfrontpoints)
2637                         {
2638                                 if (side == 0)
2639                                         AddPortalToNodes(portal, back, other_node);
2640                                 else
2641                                         AddPortalToNodes(portal, other_node, back);
2642                                 continue;
2643                         }
2644                         if (!numbackpoints)
2645                         {
2646                                 if (side == 0)
2647                                         AddPortalToNodes(portal, front, other_node);
2648                                 else
2649                                         AddPortalToNodes(portal, other_node, front);
2650                                 continue;
2651                         }
2652
2653                         // the portal is split
2654                         splitportal = AllocPortal();
2655                         temp = splitportal->chain;
2656                         *splitportal = *portal;
2657                         splitportal->chain = temp;
2658                         for (i = 0;i < numbackpoints*3;i++)
2659                                 splitportal->points[i] = backpoints[i];
2660                         splitportal->numpoints = numbackpoints;
2661                         for (i = 0;i < numfrontpoints*3;i++)
2662                                 portal->points[i] = frontpoints[i];
2663                         portal->numpoints = numfrontpoints;
2664
2665                         if (side == 0)
2666                         {
2667                                 AddPortalToNodes(portal, front, other_node);
2668                                 AddPortalToNodes(splitportal, back, other_node);
2669                         }
2670                         else
2671                         {
2672                                 AddPortalToNodes(portal, other_node, front);
2673                                 AddPortalToNodes(splitportal, other_node, back);
2674                         }
2675                 }
2676         }
2677
2678         Mod_Q1BSP_RecursiveNodePortals(front);
2679         Mod_Q1BSP_RecursiveNodePortals(back);
2680 }
2681
2682 static void Mod_Q1BSP_MakePortals(void)
2683 {
2684         portalchain = NULL;
2685         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brush.data_nodes);
2686         Mod_Q1BSP_FinalizePortals();
2687 }
2688
2689 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2690 {
2691         int i, j, stylecounts[256], totalcount, remapstyles[256];
2692         msurface_t *surface;
2693         memset(stylecounts, 0, sizeof(stylecounts));
2694         for (i = 0;i < model->nummodelsurfaces;i++)
2695         {
2696                 surface = model->brush.data_surfaces + model->firstmodelsurface + i;
2697                 for (j = 0;j < MAXLIGHTMAPS;j++)
2698                         stylecounts[surface->styles[j]]++;
2699         }
2700         totalcount = 0;
2701         model->brushq1.light_styles = 0;
2702         for (i = 0;i < 255;i++)
2703         {
2704                 if (stylecounts[i])
2705                 {
2706                         remapstyles[i] = model->brushq1.light_styles++;
2707                         totalcount += stylecounts[i] + 1;
2708                 }
2709         }
2710         if (!totalcount)
2711                 return;
2712         model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2713         model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2714         model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2715         model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2716         model->brushq1.light_styles = 0;
2717         for (i = 0;i < 255;i++)
2718                 if (stylecounts[i])
2719                         model->brushq1.light_style[model->brushq1.light_styles++] = i;
2720         j = 0;
2721         for (i = 0;i < model->brushq1.light_styles;i++)
2722         {
2723                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2724                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2725         }
2726         for (i = 0;i < model->nummodelsurfaces;i++)
2727         {
2728                 surface = model->brush.data_surfaces + model->firstmodelsurface + i;
2729                 for (j = 0;j < MAXLIGHTMAPS;j++)
2730                         if (surface->styles[j] != 255)
2731                                 *model->brushq1.light_styleupdatechains[remapstyles[surface->styles[j]]]++ = surface;
2732         }
2733         j = 0;
2734         for (i = 0;i < model->brushq1.light_styles;i++)
2735         {
2736                 *model->brushq1.light_styleupdatechains[i] = NULL;
2737                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2738                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2739         }
2740 }
2741
2742 //Returns PVS data for a given point
2743 //(note: can return NULL)
2744 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2745 {
2746         mnode_t *node;
2747         Mod_CheckLoaded(model);
2748         node = model->brush.data_nodes;
2749         while (node->plane)
2750                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2751         if (((mleaf_t *)node)->clusterindex >= 0)
2752                 return model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2753         else
2754                 return NULL;
2755 }
2756
2757 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2758 {
2759         while (node->plane)
2760         {
2761                 float d = PlaneDiff(org, node->plane);
2762                 if (d > radius)
2763                         node = node->children[0];
2764                 else if (d < -radius)
2765                         node = node->children[1];
2766                 else
2767                 {
2768                         // go down both sides
2769                         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2770                         node = node->children[1];
2771                 }
2772         }
2773         // if this leaf is in a cluster, accumulate the pvs bits
2774         if (((mleaf_t *)node)->clusterindex >= 0)
2775         {
2776                 int i;
2777                 qbyte *pvs = model->brush.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brush.num_pvsclusterbytes;
2778                 for (i = 0;i < pvsbytes;i++)
2779                         pvsbuffer[i] |= pvs[i];
2780         }
2781 }
2782
2783 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2784 //of the given point.
2785 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2786 {
2787         int bytes = ((model->brush.num_leafs - 1) + 7) >> 3;
2788         bytes = min(bytes, pvsbufferlength);
2789         if (r_novis.integer || !Mod_Q1BSP_GetPVS(model, org))
2790         {
2791                 memset(pvsbuffer, 0xFF, bytes);
2792                 return bytes;
2793         }
2794         memset(pvsbuffer, 0, bytes);
2795         Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brush.data_nodes);
2796         return bytes;
2797 }
2798
2799 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2800 {
2801         vec3_t size;
2802         const hull_t *hull;
2803
2804         VectorSubtract(inmaxs, inmins, size);
2805         if (cmodel->brush.ishlbsp)
2806         {
2807                 if (size[0] < 3)
2808                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2809                 else if (size[0] <= 32)
2810                 {
2811                         if (size[2] < 54) // pick the nearest of 36 or 72
2812                                 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2813                         else
2814                                 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2815                 }
2816                 else
2817                         hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2818         }
2819         else
2820         {
2821                 if (size[0] < 3)
2822                         hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2823                 else if (size[0] <= 32)
2824                         hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2825                 else
2826                         hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2827         }
2828         VectorCopy(inmins, outmins);
2829         VectorAdd(inmins, hull->clip_size, outmaxs);
2830 }
2831
2832 /*
2833 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)
2834 {
2835         mleaf_t *leaf;
2836         for (;;)
2837         {
2838                 if (!BoxesOverlap(node->mins, node->maxs, mins, maxs))
2839                         return;
2840                 if (!node->plane)
2841                         break;
2842                 Mod_Q1BSP_RecursiveGetVisible(node->children[0], model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces, pvs);
2843                 node = node->children[1];
2844         }
2845         leaf = (mleaf_t *)node;
2846         if ((pvs == NULL || CHECKPVSBIT(pvs, leaf->clusterindex)))
2847         {
2848                 int leafsurfacenum;
2849                 msurface_t *surface;
2850                 if (maxleafs && *numleafs < maxleafs)
2851                         leaflist[(*numleafs)++] = leaf;
2852                 if (maxsurfaces)
2853                 {
2854                         for (leafsurfacenum = 0;leafsurfacenum < leaf->numleafsurfaces;leafsurfacenum++)
2855                         {
2856                                 surface = model->brush.data_surfaces + leaf->firstleafsurface[leafsurfacenum];
2857                                 if (surface->shadowmark != shadowmarkcount)
2858                                 {
2859                                         surface->shadowmark = shadowmarkcount;
2860                                         if (BoxesOverlap(mins, maxs, surface->mins, surface->maxs) && ((surface->flags & SURF_PLANEBACK) ? PlaneDiff(point, surface->plane) < 0 : PlaneDiff(point, surface->plane) > 0) && *numsurfaces < maxsurfaces)
2861                                                 surfacelist[(*numsurfaces)++] = surface;
2862                                 }
2863                         }
2864                 }
2865         }
2866 }
2867
2868 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)
2869 {
2870         // FIXME: support portals
2871         if (maxsurfaces)
2872                 *numsurfaces = 0;
2873         if (maxleafs)
2874                 *numleafs = 0;
2875         pvs = ent->model->brush.GetPVS(ent->model, relativelightorigin);
2876         Mod_Q1BSP_RecursiveGetVisible(ent->model->brush.data_nodes + ent->model->brushq1.firstclipnode, model, point, mins, maxs, maxleafs, leaflist, numleafs, maxsurfaces, surfacelist, numsurfaces);
2877 }
2878 */
2879
2880 extern void R_Q1BSP_DrawSky(entity_render_t *ent);
2881 extern void R_Q1BSP_Draw(entity_render_t *ent);
2882 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);
2883 extern void R_Q1BSP_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int numsurfaces, const int *surfacelist);
2884 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);
2885 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2886 {
2887         int i, j, k;
2888         dheader_t *header;
2889         dmodel_t *bm;
2890         mempool_t *mainmempool;
2891         float dist, modelyawradius, modelradius, *vec;
2892         msurface_t *surface;
2893         int numshadowmeshtriangles;
2894
2895         mod->type = mod_brushq1;
2896
2897         header = (dheader_t *)buffer;
2898
2899         i = LittleLong(header->version);
2900         if (i != BSPVERSION && i != 30)
2901                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2902         mod->brush.ishlbsp = i == 30;
2903
2904         mod->soundfromcenter = true;
2905         mod->TraceBox = Mod_Q1BSP_TraceBox;
2906         mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2907         mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2908         mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2909         mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2910         mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2911         mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2912         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2913         mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2914         mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2915         mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2916
2917         if (loadmodel->isworldmodel)
2918                 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2919
2920 // swap all the lumps
2921         mod_base = (qbyte *)header;
2922
2923         header->version = LittleLong(header->version);
2924         for (i = 0;i < HEADER_LUMPS;i++)
2925         {
2926                 header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
2927                 header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
2928         }
2929
2930 // load into heap
2931
2932         // store which lightmap format to use
2933         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2934
2935         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2936         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2937         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2938         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2939         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2940         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2941         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2942         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2943         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2944         Mod_Q1BSP_LoadLeaffaces(&header->lumps[LUMP_MARKSURFACES]);
2945         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2946         // load submodels before leafs because they contain the number of vis leafs
2947         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2948         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2949         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2950         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2951
2952         if (!mod->brushq1.lightdata)
2953                 mod->brush.LightPoint = NULL;
2954
2955         if (mod->brushq1.data_compressedpvs)
2956                 Mem_Free(mod->brushq1.data_compressedpvs);
2957         mod->brushq1.data_compressedpvs = NULL;
2958         mod->brushq1.num_compressedpvs = 0;
2959
2960         Mod_Q1BSP_MakeHull0();
2961         Mod_Q1BSP_MakePortals();
2962
2963         mod->numframes = 2;             // regular and alternate animation
2964         mod->numskins = 1;
2965
2966         mainmempool = mod->mempool;
2967
2968         Mod_Q1BSP_LoadLightList();
2969
2970         // make a single combined shadow mesh to allow optimized shadow volume creation
2971         numshadowmeshtriangles = 0;
2972         for (j = 0, surface = loadmodel->brush.data_surfaces;j < loadmodel->brush.num_surfaces;j++, surface++)
2973         {
2974                 surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
2975                 numshadowmeshtriangles += surface->mesh.num_triangles;
2976         }
2977         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
2978         for (j = 0, surface = loadmodel->brush.data_surfaces;j < loadmodel->brush.num_surfaces;j++, surface++)
2979                 Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, surface->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->mesh.num_triangles, surface->mesh.data_element3i);
2980         loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true);
2981         Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
2982
2983         if (loadmodel->brush.numsubmodels)
2984                 loadmodel->brush.submodels = Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(model_t *));
2985
2986         // LordHavoc: to clear the fog around the original quake submodel code, I
2987         // will explain:
2988         // first of all, some background info on the submodels:
2989         // model 0 is the map model (the world, named maps/e1m1.bsp for example)
2990         // model 1 and higher are submodels (doors and the like, named *1, *2, etc)
2991         // now the weird for loop itself:
2992         // the loop functions in an odd way, on each iteration it sets up the
2993         // current 'mod' model (which despite the confusing code IS the model of
2994         // the number i), at the end of the loop it duplicates the model to become
2995         // the next submodel, and loops back to set up the new submodel.
2996
2997         // LordHavoc: now the explanation of my sane way (which works identically):
2998         // set up the world model, then on each submodel copy from the world model
2999         // and set up the submodel with the respective model info.
3000         for (i = 0;i < mod->brush.numsubmodels;i++)
3001         {
3002                 // LordHavoc: this code was originally at the end of this loop, but
3003                 // has been transformed to something more readable at the start here.
3004
3005                 if (i > 0)
3006                 {
3007                         char name[10];
3008                         // LordHavoc: only register submodels if it is the world
3009                         // (prevents external bsp models from replacing world submodels with
3010                         //  their own)
3011                         if (!loadmodel->isworldmodel)
3012                                 continue;
3013                         // duplicate the basic information
3014                         sprintf(name, "*%i", i);
3015                         mod = Mod_FindName(name);
3016                         // copy the base model to this one
3017                         *mod = *loadmodel;
3018                         // rename the clone back to its proper name
3019                         strcpy(mod->name, name);
3020                         // textures and memory belong to the main model
3021                         mod->texturepool = NULL;
3022                         mod->mempool = NULL;
3023                 }
3024
3025                 mod->brush.submodel = i;
3026
3027                 if (loadmodel->brush.submodels)
3028                         loadmodel->brush.submodels[i] = mod;
3029
3030                 bm = &mod->brushq1.submodels[i];
3031
3032                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
3033                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3034                 {
3035                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
3036                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
3037                 }
3038
3039                 mod->firstmodelsurface = bm->firstface;
3040                 mod->nummodelsurfaces = bm->numfaces;
3041
3042                 // make the model surface list (used by shadowing/lighting)
3043                 mod->surfacelist = Mem_Alloc(loadmodel->mempool, mod->nummodelsurfaces * sizeof(*mod->surfacelist));
3044                 for (j = 0;j < mod->nummodelsurfaces;j++)
3045                         mod->surfacelist[j] = mod->firstmodelsurface + j;
3046
3047                 // this gets altered below if sky is used
3048                 mod->DrawSky = NULL;
3049                 mod->Draw = R_Q1BSP_Draw;
3050                 mod->GetLightInfo = R_Q1BSP_GetLightInfo;
3051                 mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
3052                 mod->DrawLight = R_Q1BSP_DrawLight;
3053                 if (i != 0)
3054                 {
3055                         mod->brush.GetPVS = NULL;
3056                         mod->brush.FatPVS = NULL;
3057                         mod->brush.BoxTouchingPVS = NULL;
3058                         mod->brush.LightPoint = NULL;
3059                         mod->brush.AmbientSoundLevelsForPoint = NULL;
3060                 }
3061                 Mod_Q1BSP_BuildLightmapUpdateChains(loadmodel->mempool, mod);
3062                 if (mod->nummodelsurfaces)
3063                 {
3064                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3065                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3066                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3067                         modelyawradius = 0;
3068                         modelradius = 0;
3069                         for (j = 0, surface = &mod->brush.data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++)
3070                         {
3071                                 // we only need to have a drawsky function if it is used(usually only on world model)
3072                                 if (surface->texture->flags & SURF_DRAWSKY)
3073                                         mod->DrawSky = R_Q1BSP_DrawSky;
3074                                 // LordHavoc: submodels always clip, even if water
3075                                 if (i)
3076                                         surface->flags |= SURF_SOLIDCLIP;
3077                                 // calculate bounding shapes
3078                                 for (k = 0, vec = surface->mesh.data_vertex3f;k < surface->mesh.num_vertices;k++, vec += 3)
3079                                 {
3080                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3081                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3082                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3083                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3084                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3085                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3086                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3087                                         if (modelyawradius < dist)
3088                                                 modelyawradius = dist;
3089                                         dist += vec[2]*vec[2];
3090                                         if (modelradius < dist)
3091                                                 modelradius = dist;
3092                                 }
3093                         }
3094                         modelyawradius = sqrt(modelyawradius);
3095                         modelradius = sqrt(modelradius);
3096                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3097                         mod->yawmins[2] = mod->normalmins[2];
3098                         mod->yawmaxs[2] = mod->normalmaxs[2];
3099                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3100                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3101                         mod->radius = modelradius;
3102                         mod->radius2 = modelradius * modelradius;
3103                 }
3104                 else
3105                 {
3106                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3107                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name);
3108                 }
3109                 //mod->brushq1.num_visleafs = bm->visleafs;
3110         }
3111
3112         Mod_Q1BSP_LoadMapBrushes();
3113
3114         //Mod_Q1BSP_ProcessLightList();
3115
3116         if (developer.integer)
3117                 Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brush.num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brushq1.submodels[i].visleafs, loadmodel->brush.num_portals);
3118 }
3119
3120 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3121 {
3122 }
3123
3124 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3125 {
3126 /*
3127         d_t *in;
3128         m_t *out;
3129         int i, count;
3130
3131         in = (void *)(mod_base + l->fileofs);
3132         if (l->filelen % sizeof(*in))
3133                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3134         count = l->filelen / sizeof(*in);
3135         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3136
3137         loadmodel-> = out;
3138         loadmodel->num = count;
3139
3140         for (i = 0;i < count;i++, in++, out++)
3141         {
3142         }
3143 */
3144 }
3145
3146 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3147 {
3148 /*
3149         d_t *in;
3150         m_t *out;
3151         int i, count;
3152
3153         in = (void *)(mod_base + l->fileofs);
3154         if (l->filelen % sizeof(*in))
3155                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3156         count = l->filelen / sizeof(*in);
3157         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3158
3159         loadmodel-> = out;
3160         loadmodel->num = count;
3161
3162         for (i = 0;i < count;i++, in++, out++)
3163         {
3164         }
3165 */
3166 }
3167
3168 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3169 {
3170 /*
3171         d_t *in;
3172         m_t *out;
3173         int i, count;
3174
3175         in = (void *)(mod_base + l->fileofs);
3176         if (l->filelen % sizeof(*in))
3177                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3178         count = l->filelen / sizeof(*in);
3179         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3180
3181         loadmodel-> = out;
3182         loadmodel->num = count;
3183
3184         for (i = 0;i < count;i++, in++, out++)
3185         {
3186         }
3187 */
3188 }
3189
3190 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3191 {
3192 /*
3193         d_t *in;
3194         m_t *out;
3195         int i, count;
3196
3197         in = (void *)(mod_base + l->fileofs);
3198         if (l->filelen % sizeof(*in))
3199                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3200         count = l->filelen / sizeof(*in);
3201         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3202
3203         loadmodel-> = out;
3204         loadmodel->num = count;
3205
3206         for (i = 0;i < count;i++, in++, out++)
3207         {
3208         }
3209 */
3210 }
3211
3212 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3213 {
3214 /*
3215         d_t *in;
3216         m_t *out;
3217         int i, count;
3218
3219         in = (void *)(mod_base + l->fileofs);
3220         if (l->filelen % sizeof(*in))
3221                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3222         count = l->filelen / sizeof(*in);
3223         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3224
3225         loadmodel-> = out;
3226         loadmodel->num = count;
3227
3228         for (i = 0;i < count;i++, in++, out++)
3229         {
3230         }
3231 */
3232 }
3233
3234 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3235 {
3236 /*
3237         d_t *in;
3238         m_t *out;
3239         int i, count;
3240
3241         in = (void *)(mod_base + l->fileofs);
3242         if (l->filelen % sizeof(*in))
3243                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3244         count = l->filelen / sizeof(*in);
3245         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3246
3247         loadmodel-> = out;
3248         loadmodel->num = count;
3249
3250         for (i = 0;i < count;i++, in++, out++)
3251         {
3252         }
3253 */
3254 }
3255
3256 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3257 {
3258 /*
3259         d_t *in;
3260         m_t *out;
3261         int i, count;
3262
3263         in = (void *)(mod_base + l->fileofs);
3264         if (l->filelen % sizeof(*in))
3265                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3266         count = l->filelen / sizeof(*in);
3267         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3268
3269         loadmodel-> = out;
3270         loadmodel->num = count;
3271
3272         for (i = 0;i < count;i++, in++, out++)
3273         {
3274         }
3275 */
3276 }
3277
3278 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3279 {
3280 /*
3281         d_t *in;
3282         m_t *out;
3283         int i, count;
3284
3285         in = (void *)(mod_base + l->fileofs);
3286         if (l->filelen % sizeof(*in))
3287                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3288         count = l->filelen / sizeof(*in);
3289         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3290
3291         loadmodel-> = out;
3292         loadmodel->num = count;
3293
3294         for (i = 0;i < count;i++, in++, out++)
3295         {
3296         }
3297 */
3298 }
3299
3300 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3301 {
3302 /*
3303         d_t *in;
3304         m_t *out;
3305         int i, count;
3306
3307         in = (void *)(mod_base + l->fileofs);
3308         if (l->filelen % sizeof(*in))
3309                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3310         count = l->filelen / sizeof(*in);
3311         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3312
3313         loadmodel-> = out;
3314         loadmodel->num = count;
3315
3316         for (i = 0;i < count;i++, in++, out++)
3317         {
3318         }
3319 */
3320 }
3321
3322 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3323 {
3324 /*
3325         d_t *in;
3326         m_t *out;
3327         int i, count;
3328
3329         in = (void *)(mod_base + l->fileofs);
3330         if (l->filelen % sizeof(*in))
3331                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3332         count = l->filelen / sizeof(*in);
3333         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3334
3335         loadmodel-> = out;
3336         loadmodel->num = count;
3337
3338         for (i = 0;i < count;i++, in++, out++)
3339         {
3340         }
3341 */
3342 }
3343
3344 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3345 {
3346 /*
3347         d_t *in;
3348         m_t *out;
3349         int i, count;
3350
3351         in = (void *)(mod_base + l->fileofs);
3352         if (l->filelen % sizeof(*in))
3353                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3354         count = l->filelen / sizeof(*in);
3355         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3356
3357         loadmodel-> = out;
3358         loadmodel->num = count;
3359
3360         for (i = 0;i < count;i++, in++, out++)
3361         {
3362         }
3363 */
3364 }
3365
3366 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3367 {
3368 /*
3369         d_t *in;
3370         m_t *out;
3371         int i, count;
3372
3373         in = (void *)(mod_base + l->fileofs);
3374         if (l->filelen % sizeof(*in))
3375                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3376         count = l->filelen / sizeof(*in);
3377         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3378
3379         loadmodel-> = out;
3380         loadmodel->num = count;
3381
3382         for (i = 0;i < count;i++, in++, out++)
3383         {
3384         }
3385 */
3386 }
3387
3388 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3389 {
3390 /*
3391         d_t *in;
3392         m_t *out;
3393         int i, count;
3394
3395         in = (void *)(mod_base + l->fileofs);
3396         if (l->filelen % sizeof(*in))
3397                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3398         count = l->filelen / sizeof(*in);
3399         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3400
3401         loadmodel-> = out;
3402         loadmodel->num = count;
3403
3404         for (i = 0;i < count;i++, in++, out++)
3405         {
3406         }
3407 */
3408 }
3409
3410 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3411 {
3412 /*
3413         d_t *in;
3414         m_t *out;
3415         int i, count;
3416
3417         in = (void *)(mod_base + l->fileofs);
3418         if (l->filelen % sizeof(*in))
3419                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3420         count = l->filelen / sizeof(*in);
3421         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3422
3423         loadmodel-> = out;
3424         loadmodel->num = count;
3425
3426         for (i = 0;i < count;i++, in++, out++)
3427         {
3428         }
3429 */
3430 }
3431
3432 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3433 {
3434 /*
3435         d_t *in;
3436         m_t *out;
3437         int i, count;
3438
3439         in = (void *)(mod_base + l->fileofs);
3440         if (l->filelen % sizeof(*in))
3441                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3442         count = l->filelen / sizeof(*in);
3443         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3444
3445         loadmodel-> = out;
3446         loadmodel->num = count;
3447
3448         for (i = 0;i < count;i++, in++, out++)
3449         {
3450         }
3451 */
3452 }
3453
3454 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3455 {
3456 /*
3457         d_t *in;
3458         m_t *out;
3459         int i, count;
3460
3461         in = (void *)(mod_base + l->fileofs);
3462         if (l->filelen % sizeof(*in))
3463                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);