]> git.xonotic.org Git - xonotic/darkplaces.git/blob - model_brush.c
Q3BSP collisions implemented (although not for patches, which aren't supported anyway...
[xonotic/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22 #include "image.h"
23 #include "r_shadow.h"
24
25 // note: model_shared.c sets up r_notexture, and r_surf_notexture
26
27 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
28
29 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
30 cvar_t halflifebsp = {0, "halflifebsp", "0"};
31 cvar_t r_novis = {0, "r_novis", "0"};
32 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
33 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
34 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
35 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
36
37 void Mod_BrushInit(void)
38 {
39 //      Cvar_RegisterVariable(&r_subdivide_size);
40         Cvar_RegisterVariable(&halflifebsp);
41         Cvar_RegisterVariable(&r_novis);
42         Cvar_RegisterVariable(&r_miplightmaps);
43         Cvar_RegisterVariable(&r_lightmaprgba);
44         Cvar_RegisterVariable(&r_nosurftextures);
45         Cvar_RegisterVariable(&r_sortsurfaces);
46         memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
47 }
48
49 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
50 {
51         mnode_t *node;
52
53         if (model == NULL)
54                 return NULL;
55
56         Mod_CheckLoaded(model);
57
58         // LordHavoc: modified to start at first clip node,
59         // in other words: first node of the (sub)model
60         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
61         while (node->contents == 0)
62                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
63
64         return (mleaf_t *)node;
65 }
66
67 /*
68 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
69 {
70         mnode_t *node;
71
72         if (model == NULL)
73                 return CONTENTS_EMPTY;
74
75         Mod_CheckLoaded(model);
76
77         // LordHavoc: modified to start at first clip node,
78         // in other words: first node of the (sub)model
79         node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
80         while (node->contents == 0)
81                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
82
83         return ((mleaf_t *)node)->contents;
84 }
85 */
86
87 typedef struct findnonsolidlocationinfo_s
88 {
89         vec3_t center;
90         vec_t radius;
91         vec3_t nudge;
92         vec_t bestdist;
93         model_t *model;
94 }
95 findnonsolidlocationinfo_t;
96
97 #if 0
98 extern cvar_t samelevel;
99 #endif
100 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
101 {
102         int i, surfnum, k, *tri, *mark;
103         float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
104 #if 0
105         float surfnormal[3];
106 #endif
107         msurface_t *surf;
108         surfmesh_t *mesh;
109         for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
110         {
111                 surf = info->model->brushq1.surfaces + *mark;
112                 if (surf->flags & SURF_SOLIDCLIP)
113                 {
114 #if 0
115                         VectorCopy(surf->plane->normal, surfnormal);
116                         if (surf->flags & SURF_PLANEBACK)
117                                 VectorNegate(surfnormal, surfnormal);
118 #endif
119                         for (mesh = surf->mesh;mesh;mesh = mesh->chain)
120                         {
121                                 for (k = 0;k < mesh->numtriangles;k++)
122                                 {
123                                         tri = mesh->element3i + k * 3;
124                                         VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
125                                         VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
126                                         VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
127                                         VectorSubtract(vert[1], vert[0], edge[0]);
128                                         VectorSubtract(vert[2], vert[1], edge[1]);
129                                         CrossProduct(edge[1], edge[0], facenormal);
130                                         if (facenormal[0] || facenormal[1] || facenormal[2])
131                                         {
132                                                 VectorNormalize(facenormal);
133 #if 0
134                                                 if (VectorDistance(facenormal, surfnormal) > 0.01f)
135                                                         Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
136 #endif
137                                                 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
138                                                 if (f <= info->bestdist && f >= -info->bestdist)
139                                                 {
140                                                         VectorSubtract(vert[0], vert[2], edge[2]);
141                                                         VectorNormalize(edge[0]);
142                                                         VectorNormalize(edge[1]);
143                                                         VectorNormalize(edge[2]);
144                                                         CrossProduct(facenormal, edge[0], edgenormal[0]);
145                                                         CrossProduct(facenormal, edge[1], edgenormal[1]);
146                                                         CrossProduct(facenormal, edge[2], edgenormal[2]);
147 #if 0
148                                                         if (samelevel.integer & 1)
149                                                                 VectorNegate(edgenormal[0], edgenormal[0]);
150                                                         if (samelevel.integer & 2)
151                                                                 VectorNegate(edgenormal[1], edgenormal[1]);
152                                                         if (samelevel.integer & 4)
153                                                                 VectorNegate(edgenormal[2], edgenormal[2]);
154                                                         for (i = 0;i < 3;i++)
155                                                                 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
156                                                                  || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
157                                                                  || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
158                                                                         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]);
159 #endif
160                                                         // face distance
161                                                         if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
162                                                          && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
163                                                          && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
164                                                         {
165                                                                 // we got lucky, the center is within the face
166                                                                 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
167                                                                 if (dist < 0)
168                                                                 {
169                                                                         dist = -dist;
170                                                                         if (info->bestdist > dist)
171                                                                         {
172                                                                                 info->bestdist = dist;
173                                                                                 VectorScale(facenormal, (info->radius - -dist), info->nudge);
174                                                                         }
175                                                                 }
176                                                                 else
177                                                                 {
178                                                                         if (info->bestdist > dist)
179                                                                         {
180                                                                                 info->bestdist = dist;
181                                                                                 VectorScale(facenormal, (info->radius - dist), info->nudge);
182                                                                         }
183                                                                 }
184                                                         }
185                                                         else
186                                                         {
187                                                                 // check which edge or vertex the center is nearest
188                                                                 for (i = 0;i < 3;i++)
189                                                                 {
190                                                                         f = DotProduct(info->center, edge[i]);
191                                                                         if (f >= DotProduct(vert[0], edge[i])
192                                                                          && f <= DotProduct(vert[1], edge[i]))
193                                                                         {
194                                                                                 // on edge
195                                                                                 VectorMA(info->center, -f, edge[i], point);
196                                                                                 dist = sqrt(DotProduct(point, point));
197                                                                                 if (info->bestdist > dist)
198                                                                                 {
199                                                                                         info->bestdist = dist;
200                                                                                         VectorScale(point, (info->radius / dist), info->nudge);
201                                                                                 }
202                                                                                 // skip both vertex checks
203                                                                                 // (both are further away than this edge)
204                                                                                 i++;
205                                                                         }
206                                                                         else
207                                                                         {
208                                                                                 // not on edge, check first vertex of edge
209                                                                                 VectorSubtract(info->center, vert[i], point);
210                                                                                 dist = sqrt(DotProduct(point, point));
211                                                                                 if (info->bestdist > dist)
212                                                                                 {
213                                                                                         info->bestdist = dist;
214                                                                                         VectorScale(point, (info->radius / dist), info->nudge);
215                                                                                 }
216                                                                         }
217                                                                 }
218                                                         }
219                                                 }
220                                         }
221                                 }
222                         }
223                 }
224         }
225 }
226
227 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
228 {
229         if (node->contents)
230         {
231                 if (((mleaf_t *)node)->nummarksurfaces)
232                         Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
233         }
234         else
235         {
236                 float f = PlaneDiff(info->center, node->plane);
237                 if (f >= -info->bestdist)
238                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
239                 if (f <= info->bestdist)
240                         Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
241         }
242 }
243
244 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
245 {
246         int i;
247         findnonsolidlocationinfo_t info;
248         if (model == NULL)
249         {
250                 VectorCopy(in, out);
251                 return;
252         }
253         VectorCopy(in, info.center);
254         info.radius = radius;
255         info.model = model;
256         i = 0;
257         do
258         {
259                 VectorClear(info.nudge);
260                 info.bestdist = radius;
261                 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
262                 VectorAdd(info.center, info.nudge, info.center);
263         }
264         while (info.bestdist < radius && ++i < 10);
265         VectorCopy(info.center, out);
266 }
267
268 typedef struct
269 {
270         // the hull we're tracing through
271         const hull_t *hull;
272
273         // the trace structure to fill in
274         trace_t *trace;
275
276         // start, end, and end - start (in model space)
277         double start[3];
278         double end[3];
279         double dist[3];
280 }
281 RecursiveHullCheckTraceInfo_t;
282
283 // 1/32 epsilon to keep floating point happy
284 #define DIST_EPSILON (0.03125)
285
286 #define HULLCHECKSTATE_EMPTY 0
287 #define HULLCHECKSTATE_SOLID 1
288 #define HULLCHECKSTATE_DONE 2
289
290 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
291 {
292         // status variables, these don't need to be saved on the stack when
293         // recursing...  but are because this should be thread-safe
294         // (note: tracing against a bbox is not thread-safe, yet)
295         int ret;
296         mplane_t *plane;
297         double t1, t2;
298
299         // variables that need to be stored on the stack when recursing
300         dclipnode_t *node;
301         int side;
302         double midf, mid[3];
303
304         // LordHavoc: a goto!  everyone flee in terror... :)
305 loc0:
306         // check for empty
307         if (num < 0)
308         {
309                 t->trace->endcontents = num;
310                 if (t->trace->thiscontents)
311                 {
312                         if (num == t->trace->thiscontents)
313                                 t->trace->allsolid = false;
314                         else
315                         {
316                                 // if the first leaf is solid, set startsolid
317                                 if (t->trace->allsolid)
318                                         t->trace->startsolid = true;
319                                 return HULLCHECKSTATE_SOLID;
320                         }
321                         return HULLCHECKSTATE_EMPTY;
322                 }
323                 else
324                 {
325                         if (num != CONTENTS_SOLID)
326                         {
327                                 t->trace->allsolid = false;
328                                 if (num == CONTENTS_EMPTY)
329                                         t->trace->inopen = true;
330                                 else
331                                         t->trace->inwater = true;
332                         }
333                         else
334                         {
335                                 // if the first leaf is solid, set startsolid
336                                 if (t->trace->allsolid)
337                                         t->trace->startsolid = true;
338                                 return HULLCHECKSTATE_SOLID;
339                         }
340                         return HULLCHECKSTATE_EMPTY;
341                 }
342         }
343
344         // find the point distances
345         node = t->hull->clipnodes + num;
346
347         plane = t->hull->planes + node->planenum;
348         if (plane->type < 3)
349         {
350                 t1 = p1[plane->type] - plane->dist;
351                 t2 = p2[plane->type] - plane->dist;
352         }
353         else
354         {
355                 t1 = DotProduct (plane->normal, p1) - plane->dist;
356                 t2 = DotProduct (plane->normal, p2) - plane->dist;
357         }
358
359         if (t1 < 0)
360         {
361                 if (t2 < 0)
362                 {
363                         num = node->children[1];
364                         goto loc0;
365                 }
366                 side = 1;
367         }
368         else
369         {
370                 if (t2 >= 0)
371                 {
372                         num = node->children[0];
373                         goto loc0;
374                 }
375                 side = 0;
376         }
377
378         // the line intersects, find intersection point
379         // LordHavoc: this uses the original trace for maximum accuracy
380         if (plane->type < 3)
381         {
382                 t1 = t->start[plane->type] - plane->dist;
383                 t2 = t->end[plane->type] - plane->dist;
384         }
385         else
386         {
387                 t1 = DotProduct (plane->normal, t->start) - plane->dist;
388                 t2 = DotProduct (plane->normal, t->end) - plane->dist;
389         }
390
391         midf = t1 / (t1 - t2);
392         midf = bound(p1f, midf, p2f);
393         VectorMA(t->start, midf, t->dist, mid);
394
395         // recurse both sides, front side first
396         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
397         // if this side is not empty, return what it is (solid or done)
398         if (ret != HULLCHECKSTATE_EMPTY)
399                 return ret;
400
401         ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
402         // if other side is not solid, return what it is (empty or done)
403         if (ret != HULLCHECKSTATE_SOLID)
404                 return ret;
405
406         // front is air and back is solid, this is the impact point...
407         if (side)
408         {
409                 t->trace->plane.dist = -plane->dist;
410                 VectorNegate (plane->normal, t->trace->plane.normal);
411         }
412         else
413         {
414                 t->trace->plane.dist = plane->dist;
415                 VectorCopy (plane->normal, t->trace->plane.normal);
416         }
417
418         // bias away from surface a bit
419         t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
420         t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
421
422         midf = t1 / (t1 - t2);
423         t->trace->fraction = bound(0.0f, midf, 1.0);
424
425         return HULLCHECKSTATE_DONE;
426 }
427
428 static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs)
429 {
430         // this function currently only supports same size start and end
431         double boxsize[3];
432         RecursiveHullCheckTraceInfo_t rhc;
433
434         memset(&rhc, 0, sizeof(rhc));
435         memset(trace, 0, sizeof(trace_t));
436         rhc.trace = trace;
437         rhc.trace->fraction = 1;
438         rhc.trace->allsolid = true;
439         VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
440         if (boxsize[0] < 3)
441                 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
442         else if (model->brushq1.ishlbsp)
443         {
444                 if (boxsize[0] <= 32)
445                 {
446                         if (boxsize[2] < 54) // pick the nearest of 36 or 72
447                                 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
448                         else
449                                 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
450                 }
451                 else
452                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
453         }
454         else
455         {
456                 if (boxsize[0] <= 32)
457                         rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
458                 else
459                         rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
460         }
461         VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
462         VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
463         VectorSubtract(rhc.end, rhc.start, rhc.dist);
464         Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
465 }
466
467 static qbyte *Mod_Q1BSP_DecompressVis(model_t *model, qbyte *in)
468 {
469         static qbyte decompressed[MAX_MAP_LEAFS/8];
470         int c;
471         qbyte *out;
472         int row;
473
474         row = (model->brushq1.numleafs+7)>>3;
475         out = decompressed;
476
477         do
478         {
479                 if (*in)
480                 {
481                         *out++ = *in++;
482                         continue;
483                 }
484
485                 c = in[1];
486                 in += 2;
487                 while (c)
488                 {
489                         *out++ = 0;
490                         c--;
491                 }
492         } while (out - decompressed < row);
493
494         return decompressed;
495 }
496
497 static qbyte *Mod_Q1BSP_LeafPVS(model_t *model, mleaf_t *leaf)
498 {
499         if (r_novis.integer || leaf == model->brushq1.leafs || leaf->compressed_vis == NULL)
500                 return mod_q1bsp_novis;
501         return Mod_Q1BSP_DecompressVis(model, leaf->compressed_vis);
502 }
503
504 static void Mod_Q1BSP_LoadTextures(lump_t *l)
505 {
506         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
507         miptex_t *dmiptex;
508         texture_t *tx, *tx2, *anims[10], *altanims[10];
509         dmiptexlump_t *m;
510         qbyte *data, *mtdata;
511         char name[256];
512
513         loadmodel->brushq1.textures = NULL;
514
515         if (!l->filelen)
516                 return;
517
518         m = (dmiptexlump_t *)(mod_base + l->fileofs);
519
520         m->nummiptex = LittleLong (m->nummiptex);
521
522         // add two slots for notexture walls and notexture liquids
523         loadmodel->brushq1.numtextures = m->nummiptex + 2;
524         loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
525
526         // fill out all slots with notexture
527         for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
528         {
529                 tx->number = i;
530                 strcpy(tx->name, "NO TEXTURE FOUND");
531                 tx->width = 16;
532                 tx->height = 16;
533                 tx->skin.base = r_notexture;
534                 tx->shader = &Cshader_wall_lightmap;
535                 tx->flags = SURF_SOLIDCLIP;
536                 if (i == loadmodel->brushq1.numtextures - 1)
537                 {
538                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
539                         tx->shader = &Cshader_water;
540                 }
541                 tx->currentframe = tx;
542         }
543
544         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
545         dofs = m->dataofs;
546         // LordHavoc: mostly rewritten map texture loader
547         for (i = 0;i < m->nummiptex;i++)
548         {
549                 dofs[i] = LittleLong(dofs[i]);
550                 if (dofs[i] == -1 || r_nosurftextures.integer)
551                         continue;
552                 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
553
554                 // make sure name is no more than 15 characters
555                 for (j = 0;dmiptex->name[j] && j < 15;j++)
556                         name[j] = dmiptex->name[j];
557                 name[j] = 0;
558
559                 mtwidth = LittleLong(dmiptex->width);
560                 mtheight = LittleLong(dmiptex->height);
561                 mtdata = NULL;
562                 j = LittleLong(dmiptex->offsets[0]);
563                 if (j)
564                 {
565                         // texture included
566                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
567                         {
568                                 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
569                                 continue;
570                         }
571                         mtdata = (qbyte *)dmiptex + j;
572                 }
573
574                 if ((mtwidth & 15) || (mtheight & 15))
575                         Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
576
577                 // LordHavoc: force all names to lowercase
578                 for (j = 0;name[j];j++)
579                         if (name[j] >= 'A' && name[j] <= 'Z')
580                                 name[j] += 'a' - 'A';
581
582                 tx = loadmodel->brushq1.textures + i;
583                 strcpy(tx->name, name);
584                 tx->width = mtwidth;
585                 tx->height = mtheight;
586
587                 if (!tx->name[0])
588                 {
589                         sprintf(tx->name, "unnamed%i", i);
590                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
591                 }
592
593                 // LordHavoc: HL sky textures are entirely different than quake
594                 if (!loadmodel->brushq1.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
595                 {
596                         if (loadmodel->isworldmodel)
597                         {
598                                 data = loadimagepixels(tx->name, false, 0, 0);
599                                 if (data)
600                                 {
601                                         if (image_width == 256 && image_height == 128)
602                                         {
603                                                 R_InitSky(data, 4);
604                                                 Mem_Free(data);
605                                         }
606                                         else
607                                         {
608                                                 Mem_Free(data);
609                                                 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
610                                                 if (mtdata != NULL)
611                                                         R_InitSky(mtdata, 1);
612                                         }
613                                 }
614                                 else if (mtdata != NULL)
615                                         R_InitSky(mtdata, 1);
616                         }
617                 }
618                 else
619                 {
620                         if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
621                         {
622                                 // did not find external texture, load it from the bsp or wad3
623                                 if (loadmodel->brushq1.ishlbsp)
624                                 {
625                                         // internal texture overrides wad
626                                         qbyte *pixels, *freepixels, *fogpixels;
627                                         pixels = freepixels = NULL;
628                                         if (mtdata)
629                                                 pixels = W_ConvertWAD3Texture(dmiptex);
630                                         if (pixels == NULL)
631                                                 pixels = freepixels = W_GetTexture(tx->name);
632                                         if (pixels != NULL)
633                                         {
634                                                 tx->width = image_width;
635                                                 tx->height = image_height;
636                                                 tx->skin.base = tx->skin.merged = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
637                                                 if (Image_CheckAlpha(pixels, image_width * image_height, true))
638                                                 {
639                                                         fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
640                                                         for (j = 0;j < image_width * image_height * 4;j += 4)
641                                                         {
642                                                                 fogpixels[j + 0] = 255;
643                                                                 fogpixels[j + 1] = 255;
644                                                                 fogpixels[j + 2] = 255;
645                                                                 fogpixels[j + 3] = pixels[j + 3];
646                                                         }
647                                                         tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
648                                                         Mem_Free(fogpixels);
649                                                 }
650                                         }
651                                         if (freepixels)
652                                                 Mem_Free(freepixels);
653                                 }
654                                 else if (mtdata) // texture included
655                                         Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
656                         }
657                 }
658                 if (tx->skin.base == NULL)
659                 {
660                         // no texture found
661                         tx->width = 16;
662                         tx->height = 16;
663                         tx->skin.base = r_notexture;
664                 }
665
666                 if (tx->name[0] == '*')
667                 {
668                         // turb does not block movement
669                         tx->flags &= ~SURF_SOLIDCLIP;
670                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
671                         // LordHavoc: some turbulent textures should be fullbright and solid
672                         if (!strncmp(tx->name,"*lava",5)
673                          || !strncmp(tx->name,"*teleport",9)
674                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
675                                 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
676                         else
677                                 tx->flags |= SURF_WATERALPHA;
678                         tx->shader = &Cshader_water;
679                 }
680                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
681                 {
682                         tx->flags |= SURF_DRAWSKY;
683                         tx->shader = &Cshader_sky;
684                 }
685                 else
686                 {
687                         tx->flags |= SURF_LIGHTMAP;
688                         if (!tx->skin.fog)
689                                 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
690                         tx->shader = &Cshader_wall_lightmap;
691                 }
692
693                 // start out with no animation
694                 tx->currentframe = tx;
695         }
696
697         // sequence the animations
698         for (i = 0;i < m->nummiptex;i++)
699         {
700                 tx = loadmodel->brushq1.textures + i;
701                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
702                         continue;
703                 if (tx->anim_total[0] || tx->anim_total[1])
704                         continue;       // already sequenced
705
706                 // find the number of frames in the animation
707                 memset(anims, 0, sizeof(anims));
708                 memset(altanims, 0, sizeof(altanims));
709
710                 for (j = i;j < m->nummiptex;j++)
711                 {
712                         tx2 = loadmodel->brushq1.textures + j;
713                         if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
714                                 continue;
715
716                         num = tx2->name[1];
717                         if (num >= '0' && num <= '9')
718                                 anims[num - '0'] = tx2;
719                         else if (num >= 'a' && num <= 'j')
720                                 altanims[num - 'a'] = tx2;
721                         else
722                                 Con_Printf("Bad animating texture %s\n", tx->name);
723                 }
724
725                 max = altmax = 0;
726                 for (j = 0;j < 10;j++)
727                 {
728                         if (anims[j])
729                                 max = j + 1;
730                         if (altanims[j])
731                                 altmax = j + 1;
732                 }
733                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
734
735                 incomplete = false;
736                 for (j = 0;j < max;j++)
737                 {
738                         if (!anims[j])
739                         {
740                                 Con_Printf("Missing frame %i of %s\n", j, tx->name);
741                                 incomplete = true;
742                         }
743                 }
744                 for (j = 0;j < altmax;j++)
745                 {
746                         if (!altanims[j])
747                         {
748                                 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
749                                 incomplete = true;
750                         }
751                 }
752                 if (incomplete)
753                         continue;
754
755                 if (altmax < 1)
756                 {
757                         // if there is no alternate animation, duplicate the primary
758                         // animation into the alternate
759                         altmax = max;
760                         for (k = 0;k < 10;k++)
761                                 altanims[k] = anims[k];
762                 }
763
764                 // link together the primary animation
765                 for (j = 0;j < max;j++)
766                 {
767                         tx2 = anims[j];
768                         tx2->animated = true;
769                         tx2->anim_total[0] = max;
770                         tx2->anim_total[1] = altmax;
771                         for (k = 0;k < 10;k++)
772                         {
773                                 tx2->anim_frames[0][k] = anims[k];
774                                 tx2->anim_frames[1][k] = altanims[k];
775                         }
776                 }
777
778                 // if there really is an alternate anim...
779                 if (anims[0] != altanims[0])
780                 {
781                         // link together the alternate animation
782                         for (j = 0;j < altmax;j++)
783                         {
784                                 tx2 = altanims[j];
785                                 tx2->animated = true;
786                                 // the primary/alternate are reversed here
787                                 tx2->anim_total[0] = altmax;
788                                 tx2->anim_total[1] = max;
789                                 for (k = 0;k < 10;k++)
790                                 {
791                                         tx2->anim_frames[0][k] = altanims[k];
792                                         tx2->anim_frames[1][k] = anims[k];
793                                 }
794                         }
795                 }
796         }
797 }
798
799 static void Mod_Q1BSP_LoadLighting(lump_t *l)
800 {
801         int i;
802         qbyte *in, *out, *data, d;
803         char litfilename[1024];
804         loadmodel->brushq1.lightdata = NULL;
805         if (loadmodel->brushq1.ishlbsp) // LordHavoc: load the colored lighting data straight
806         {
807                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
808                 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
809         }
810         else // LordHavoc: bsp version 29 (normal white lighting)
811         {
812                 // LordHavoc: hope is not lost yet, check for a .lit file to load
813                 strcpy(litfilename, loadmodel->name);
814                 FS_StripExtension(litfilename, litfilename);
815                 strcat(litfilename, ".lit");
816                 data = (qbyte*) FS_LoadFile(litfilename, false);
817                 if (data)
818                 {
819                         if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
820                         {
821                                 i = LittleLong(((int *)data)[1]);
822                                 if (i == 1)
823                                 {
824                                         Con_DPrintf("loaded %s\n", litfilename);
825                                         loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
826                                         memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
827                                         Mem_Free(data);
828                                         return;
829                                 }
830                                 else
831                                 {
832                                         Con_Printf("Unknown .lit file version (%d)\n", i);
833                                         Mem_Free(data);
834                                 }
835                         }
836                         else
837                         {
838                                 if (fs_filesize == 8)
839                                         Con_Printf("Empty .lit file, ignoring\n");
840                                 else
841                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
842                                 Mem_Free(data);
843                         }
844                 }
845                 // LordHavoc: oh well, expand the white lighting data
846                 if (!l->filelen)
847                         return;
848                 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
849                 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
850                 out = loadmodel->brushq1.lightdata;
851                 memcpy(in, mod_base + l->fileofs, l->filelen);
852                 for (i = 0;i < l->filelen;i++)
853                 {
854                         d = *in++;
855                         *out++ = d;
856                         *out++ = d;
857                         *out++ = d;
858                 }
859         }
860 }
861
862 static void Mod_Q1BSP_LoadLightList(void)
863 {
864         int a, n, numlights;
865         char lightsfilename[1024], *s, *t, *lightsstring;
866         mlight_t *e;
867
868         strcpy(lightsfilename, loadmodel->name);
869         FS_StripExtension(lightsfilename, lightsfilename);
870         strcat(lightsfilename, ".lights");
871         s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
872         if (s)
873         {
874                 numlights = 0;
875                 while (*s)
876                 {
877                         while (*s && *s != '\n')
878                                 s++;
879                         if (!*s)
880                         {
881                                 Mem_Free(lightsstring);
882                                 Host_Error("lights file must end with a newline\n");
883                         }
884                         s++;
885                         numlights++;
886                 }
887                 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
888                 s = lightsstring;
889                 n = 0;
890                 while (*s && n < numlights)
891                 {
892                         t = s;
893                         while (*s && *s != '\n')
894                                 s++;
895                         if (!*s)
896                         {
897                                 Mem_Free(lightsstring);
898                                 Host_Error("misparsed lights file!\n");
899                         }
900                         e = loadmodel->brushq1.lights + n;
901                         *s = 0;
902                         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);
903                         *s = '\n';
904                         if (a != 14)
905                         {
906                                 Mem_Free(lightsstring);
907                                 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
908                         }
909                         s++;
910                         n++;
911                 }
912                 if (*s)
913                 {
914                         Mem_Free(lightsstring);
915                         Host_Error("misparsed lights file!\n");
916                 }
917                 loadmodel->brushq1.numlights = numlights;
918                 Mem_Free(lightsstring);
919         }
920 }
921
922 /*
923 static int castshadowcount = 0;
924 static void Mod_Q1BSP_ProcessLightList(void)
925 {
926         int j, k, l, *mark, lnum;
927         mlight_t *e;
928         msurface_t *surf;
929         float dist;
930         mleaf_t *leaf;
931         qbyte *pvs;
932         vec3_t temp;
933         float *v, radius2;
934         for (lnum = 0, e = loadmodel->brushq1.lights;lnum < loadmodel->brushq1.numlights;lnum++, e++)
935         {
936                 e->cullradius2 = DotProduct(e->light, e->light) / (e->falloff * e->falloff * 8192.0f * 8192.0f * 2.0f * 2.0f);// + 4096.0f;
937                 if (e->cullradius2 > 4096.0f * 4096.0f)
938                         e->cullradius2 = 4096.0f * 4096.0f;
939                 e->cullradius = e->lightradius = sqrt(e->cullradius2);
940                 leaf = Mod_Q1BSP_PointInLeaf(e->origin, loadmodel);
941                 if (leaf->compressed_vis)
942                         pvs = Mod_Q1BSP_DecompressVis(leaf->compressed_vis, loadmodel);
943                 else
944                         pvs = mod_q1bsp_novis;
945                 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
946                         loadmodel->brushq1.surfacevisframes[j] = -1;
947                 for (j = 0, leaf = loadmodel->brushq1.leafs + 1;j < loadmodel->brushq1.numleafs - 1;j++, leaf++)
948                 {
949                         if (pvs[j >> 3] & (1 << (j & 7)))
950                         {
951                                 for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++)
952                                 {
953                                         surf = loadmodel->brushq1.surfaces + *mark;
954                                         if (surf->number != *mark)
955                                                 Con_Printf("%d != %d\n", surf->number, *mark);
956                                         dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
957                                         if (surf->flags & SURF_PLANEBACK)
958                                                 dist = -dist;
959                                         if (dist > 0 && dist < e->cullradius)
960                                         {
961                                                 temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
962                                                 temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
963                                                 temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
964                                                 if (DotProduct(temp, temp) < lightradius2)
965                                                         loadmodel->brushq1.surfacevisframes[*mark] = -2;
966                                         }
967                                 }
968                         }
969                 }
970                 // build list of light receiving surfaces
971                 e->numsurfaces = 0;
972                 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
973                         if (loadmodel->brushq1.surfacevisframes[j] == -2)
974                                 e->numsurfaces++;
975                 e->surfaces = NULL;
976                 if (e->numsurfaces > 0)
977                 {
978                         e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
979                         e->numsurfaces = 0;
980                         for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
981                                 if (loadmodel->brushq1.surfacevisframes[j] == -2)
982                                         e->surfaces[e->numsurfaces++] = loadmodel->brushq1.surfaces + j;
983                 }
984                 // find bounding box and sphere of lit surfaces
985                 // (these will be used for creating a shape to clip the light)
986                 radius2 = 0;
987                 for (j = 0;j < e->numsurfaces;j++)
988                 {
989                         surf = e->surfaces[j];
990                         if (j == 0)
991                         {
992                                 VectorCopy(surf->poly_verts, e->mins);
993                                 VectorCopy(surf->poly_verts, e->maxs);
994                         }
995                         for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
996                         {
997                                 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
998                                 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
999                                 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
1000                                 VectorSubtract(v, e->origin, temp);
1001                                 dist = DotProduct(temp, temp);
1002                                 if (radius2 < dist)
1003                                         radius2 = dist;
1004                         }
1005                 }
1006                 if (e->cullradius2 > radius2)
1007                 {
1008                         e->cullradius2 = radius2;
1009                         e->cullradius = sqrt(e->cullradius2);
1010                 }
1011                 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
1012                 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
1013                 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
1014                 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
1015                 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
1016                 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
1017                 // clip shadow volumes against eachother to remove unnecessary
1018                 // polygons(and sections of polygons)
1019                 {
1020                         //vec3_t polymins, polymaxs;
1021                         int maxverts = 4;
1022                         float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1023                         float f, *v0, *v1, projectdistance;
1024
1025                         e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 1024);
1026 #if 0
1027                         {
1028                         vec3_t outermins, outermaxs, innermins, innermaxs;
1029                         innermins[0] = e->mins[0] - 1;
1030                         innermins[1] = e->mins[1] - 1;
1031                         innermins[2] = e->mins[2] - 1;
1032                         innermaxs[0] = e->maxs[0] + 1;
1033                         innermaxs[1] = e->maxs[1] + 1;
1034                         innermaxs[2] = e->maxs[2] + 1;
1035                         outermins[0] = loadmodel->normalmins[0] - 1;
1036                         outermins[1] = loadmodel->normalmins[1] - 1;
1037                         outermins[2] = loadmodel->normalmins[2] - 1;
1038                         outermaxs[0] = loadmodel->normalmaxs[0] + 1;
1039                         outermaxs[1] = loadmodel->normalmaxs[1] + 1;
1040                         outermaxs[2] = loadmodel->normalmaxs[2] + 1;
1041                         // add bounding box around the whole shadow volume set,
1042                         // facing inward to limit light area, with an outer bounding box
1043                         // facing outward (this is needed by the shadow rendering method)
1044                         // X major
1045                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1046                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1047                         verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1048                         verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1049                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1050                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1051                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1052                         verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1053                         verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1054                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1055                         // X minor
1056                         verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1057                         verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1058                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1059                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1060                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1061                         verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1062                         verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1063                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1064                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1065                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1066                         // Y major
1067                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1068                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1069                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1070                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1071                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1072                         verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1073                         verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1074                         verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1075                         verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1076                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1077                         // Y minor
1078                         verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1079                         verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1080                         verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1081                         verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1082                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1083                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1084                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1085                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1086                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1087                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1088                         // Z major
1089                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1090                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
1091                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
1092                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1093                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1094                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1095                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
1096                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
1097                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1098                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1099                         // Z minor
1100                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
1101                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1102                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1103                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
1104                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1105                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
1106                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1107                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1108                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
1109                         Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1110                         }
1111 #endif
1112                         castshadowcount++;
1113                         for (j = 0;j < e->numsurfaces;j++)
1114                         {
1115                                 surf = e->surfaces[j];
1116                                 if (surf->flags & SURF_SHADOWCAST)
1117                                         surf->castshadow = castshadowcount;
1118                         }
1119                         for (j = 0;j < e->numsurfaces;j++)
1120                         {
1121                                 surf = e->surfaces[j];
1122                                 if (surf->castshadow != castshadowcount)
1123                                         continue;
1124                                 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1125                                 if (surf->flags & SURF_PLANEBACK)
1126                                         f = -f;
1127                                 projectdistance = e->lightradius;
1128                                 if (maxverts < surf->poly_numverts)
1129                                 {
1130                                         maxverts = surf->poly_numverts;
1131                                         if (verts)
1132                                                 Mem_Free(verts);
1133                                         verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1134                                 }
1135                                 // copy the original polygon, for the front cap of the volume
1136                                 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1137                                         VectorCopy(v0, v1);
1138                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1139                                 // project the original polygon, reversed, for the back cap of the volume
1140                                 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1141                                 {
1142                                         VectorSubtract(v0, e->origin, temp);
1143                                         VectorNormalize(temp);
1144                                         VectorMA(v0, projectdistance, temp, v1);
1145                                 }
1146                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1147                                 // project the shadow volume sides
1148                                 for (l = surf->poly_numverts - 1, k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;l = k, k++, v0 = v1, v1 += 3)
1149                                 {
1150                                         if (!surf->neighborsurfaces[l] || surf->neighborsurfaces[l]->castshadow != castshadowcount)
1151                                         {
1152                                                 VectorCopy(v1, &verts[0]);
1153                                                 VectorCopy(v0, &verts[3]);
1154                                                 VectorCopy(v0, &verts[6]);
1155                                                 VectorCopy(v1, &verts[9]);
1156                                                 VectorSubtract(&verts[6], e->origin, temp);
1157                                                 VectorNormalize(temp);
1158                                                 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1159                                                 VectorSubtract(&verts[9], e->origin, temp);
1160                                                 VectorNormalize(temp);
1161                                                 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1162                                                 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1163                                         }
1164                                 }
1165                         }
1166                         // build the triangle mesh
1167                         e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
1168                         {
1169                                 shadowmesh_t *mesh;
1170                                 l = 0;
1171                                 for (mesh = e->shadowvolume;mesh;mesh = mesh->next)
1172                                         l += mesh->numtriangles;
1173                                 Con_Printf("light %i shadow volume built containing %i triangles\n", lnum, l);
1174                         }
1175                 }
1176         }
1177 }
1178 */
1179
1180
1181 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1182 {
1183         loadmodel->brushq1.visdata = NULL;
1184         if (!l->filelen)
1185                 return;
1186         loadmodel->brushq1.visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1187         memcpy(loadmodel->brushq1.visdata, mod_base + l->fileofs, l->filelen);
1188 }
1189
1190 // used only for HalfLife maps
1191 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1192 {
1193         char key[128], value[4096];
1194         char wadname[128];
1195         int i, j, k;
1196         if (!data)
1197                 return;
1198         if (!COM_ParseToken(&data, false))
1199                 return; // error
1200         if (com_token[0] != '{')
1201                 return; // error
1202         while (1)
1203         {
1204                 if (!COM_ParseToken(&data, false))
1205                         return; // error
1206                 if (com_token[0] == '}')
1207                         break; // end of worldspawn
1208                 if (com_token[0] == '_')
1209                         strcpy(key, com_token + 1);
1210                 else
1211                         strcpy(key, com_token);
1212                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1213                         key[strlen(key)-1] = 0;
1214                 if (!COM_ParseToken(&data, false))
1215                         return; // error
1216                 strcpy(value, com_token);
1217                 if (!strcmp("wad", key)) // for HalfLife maps
1218                 {
1219                         if (loadmodel->brushq1.ishlbsp)
1220                         {
1221                                 j = 0;
1222                                 for (i = 0;i < 4096;i++)
1223                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1224                                                 break;
1225                                 if (value[i])
1226                                 {
1227                                         for (;i < 4096;i++)
1228                                         {
1229                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1230                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1231                                                         j = i+1;
1232                                                 else if (value[i] == ';' || value[i] == 0)
1233                                                 {
1234                                                         k = value[i];
1235                                                         value[i] = 0;
1236                                                         strcpy(wadname, "textures/");
1237                                                         strcat(wadname, &value[j]);
1238                                                         W_LoadTextureWadFile(wadname, false);
1239                                                         j = i+1;
1240                                                         if (!k)
1241                                                                 break;
1242                                                 }
1243                                         }
1244                                 }
1245                         }
1246                 }
1247         }
1248 }
1249
1250 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1251 {
1252         loadmodel->brush.entities = NULL;
1253         if (!l->filelen)
1254                 return;
1255         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1256         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1257         if (loadmodel->brushq1.ishlbsp)
1258                 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1259 }
1260
1261
1262 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1263 {
1264         dvertex_t       *in;
1265         mvertex_t       *out;
1266         int                     i, count;
1267
1268         in = (void *)(mod_base + l->fileofs);
1269         if (l->filelen % sizeof(*in))
1270                 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1271         count = l->filelen / sizeof(*in);
1272         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1273
1274         loadmodel->brushq1.vertexes = out;
1275         loadmodel->brushq1.numvertexes = count;
1276
1277         for ( i=0 ; i<count ; i++, in++, out++)
1278         {
1279                 out->position[0] = LittleFloat(in->point[0]);
1280                 out->position[1] = LittleFloat(in->point[1]);
1281                 out->position[2] = LittleFloat(in->point[2]);
1282         }
1283 }
1284
1285 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1286 {
1287         dmodel_t        *in;
1288         dmodel_t        *out;
1289         int                     i, j, count;
1290
1291         in = (void *)(mod_base + l->fileofs);
1292         if (l->filelen % sizeof(*in))
1293                 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1294         count = l->filelen / sizeof(*in);
1295         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1296
1297         loadmodel->brushq1.submodels = out;
1298         loadmodel->brushq1.numsubmodels = count;
1299
1300         for ( i=0 ; i<count ; i++, in++, out++)
1301         {
1302                 for (j=0 ; j<3 ; j++)
1303                 {
1304                         // spread the mins / maxs by a pixel
1305                         out->mins[j] = LittleFloat(in->mins[j]) - 1;
1306                         out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1307                         out->origin[j] = LittleFloat(in->origin[j]);
1308                 }
1309                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1310                         out->headnode[j] = LittleLong(in->headnode[j]);
1311                 out->visleafs = LittleLong(in->visleafs);
1312                 out->firstface = LittleLong(in->firstface);
1313                 out->numfaces = LittleLong(in->numfaces);
1314         }
1315 }
1316
1317 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1318 {
1319         dedge_t *in;
1320         medge_t *out;
1321         int     i, count;
1322
1323         in = (void *)(mod_base + l->fileofs);
1324         if (l->filelen % sizeof(*in))
1325                 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1326         count = l->filelen / sizeof(*in);
1327         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1328
1329         loadmodel->brushq1.edges = out;
1330         loadmodel->brushq1.numedges = count;
1331
1332         for ( i=0 ; i<count ; i++, in++, out++)
1333         {
1334                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1335                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1336         }
1337 }
1338
1339 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1340 {
1341         texinfo_t *in;
1342         mtexinfo_t *out;
1343         int i, j, k, count, miptex;
1344
1345         in = (void *)(mod_base + l->fileofs);
1346         if (l->filelen % sizeof(*in))
1347                 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1348         count = l->filelen / sizeof(*in);
1349         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1350
1351         loadmodel->brushq1.texinfo = out;
1352         loadmodel->brushq1.numtexinfo = count;
1353
1354         for (i = 0;i < count;i++, in++, out++)
1355         {
1356                 for (k = 0;k < 2;k++)
1357                         for (j = 0;j < 4;j++)
1358                                 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1359
1360                 miptex = LittleLong(in->miptex);
1361                 out->flags = LittleLong(in->flags);
1362
1363                 out->texture = NULL;
1364                 if (loadmodel->brushq1.textures)
1365                 {
1366                         if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1367                                 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1368                         else
1369                                 out->texture = loadmodel->brushq1.textures + miptex;
1370                 }
1371                 if (out->flags & TEX_SPECIAL)
1372                 {
1373                         // if texture chosen is NULL or the shader needs a lightmap,
1374                         // force to notexture water shader
1375                         if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1376                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1377                 }
1378                 else
1379                 {
1380                         // if texture chosen is NULL, force to notexture
1381                         if (out->texture == NULL)
1382                                 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1383                 }
1384         }
1385 }
1386
1387 #if 0
1388 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1389 {
1390         int             i, j;
1391         float   *v;
1392
1393         mins[0] = mins[1] = mins[2] = 9999;
1394         maxs[0] = maxs[1] = maxs[2] = -9999;
1395         v = verts;
1396         for (i = 0;i < numverts;i++)
1397         {
1398                 for (j = 0;j < 3;j++, v++)
1399                 {
1400                         if (*v < mins[j])
1401                                 mins[j] = *v;
1402                         if (*v > maxs[j])
1403                                 maxs[j] = *v;
1404                 }
1405         }
1406 }
1407
1408 #define MAX_SUBDIVPOLYTRIANGLES 4096
1409 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1410
1411 static int subdivpolyverts, subdivpolytriangles;
1412 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1413 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1414
1415 static int subdivpolylookupvert(vec3_t v)
1416 {
1417         int i;
1418         for (i = 0;i < subdivpolyverts;i++)
1419                 if (subdivpolyvert[i][0] == v[0]
1420                  && subdivpolyvert[i][1] == v[1]
1421                  && subdivpolyvert[i][2] == v[2])
1422                         return i;
1423         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1424                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1425         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1426         return subdivpolyverts++;
1427 }
1428
1429 static void SubdividePolygon(int numverts, float *verts)
1430 {
1431         int             i, i1, i2, i3, f, b, c, p;
1432         vec3_t  mins, maxs, front[256], back[256];
1433         float   m, *pv, *cv, dist[256], frac;
1434
1435         if (numverts > 250)
1436                 Host_Error("SubdividePolygon: ran out of verts in buffer");
1437
1438         BoundPoly(numverts, verts, mins, maxs);
1439
1440         for (i = 0;i < 3;i++)
1441         {
1442                 m = (mins[i] + maxs[i]) * 0.5;
1443                 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1444                 if (maxs[i] - m < 8)
1445                         continue;
1446                 if (m - mins[i] < 8)
1447                         continue;
1448
1449                 // cut it
1450                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1451                         dist[c] = cv[i] - m;
1452
1453                 f = b = 0;
1454                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1455                 {
1456                         if (dist[p] >= 0)
1457                         {
1458                                 VectorCopy(pv, front[f]);
1459                                 f++;
1460                         }
1461                         if (dist[p] <= 0)
1462                         {
1463                                 VectorCopy(pv, back[b]);
1464                                 b++;
1465                         }
1466                         if (dist[p] == 0 || dist[c] == 0)
1467                                 continue;
1468                         if ((dist[p] > 0) != (dist[c] > 0) )
1469                         {
1470                                 // clip point
1471                                 frac = dist[p] / (dist[p] - dist[c]);
1472                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1473                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1474                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1475                                 f++;
1476                                 b++;
1477                         }
1478                 }
1479
1480                 SubdividePolygon(f, front[0]);
1481                 SubdividePolygon(b, back[0]);
1482                 return;
1483         }
1484
1485         i1 = subdivpolylookupvert(verts);
1486         i2 = subdivpolylookupvert(verts + 3);
1487         for (i = 2;i < numverts;i++)
1488         {
1489                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1490                 {
1491                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1492                         return;
1493                 }
1494
1495                 i3 = subdivpolylookupvert(verts + i * 3);
1496                 subdivpolyindex[subdivpolytriangles][0] = i1;
1497                 subdivpolyindex[subdivpolytriangles][1] = i2;
1498                 subdivpolyindex[subdivpolytriangles][2] = i3;
1499                 i2 = i3;
1500                 subdivpolytriangles++;
1501         }
1502 }
1503
1504 //Breaks a polygon up along axial 64 unit
1505 //boundaries so that turbulent and sky warps
1506 //can be done reasonably.
1507 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1508 {
1509         int i, j;
1510         surfvertex_t *v;
1511         surfmesh_t *mesh;
1512
1513         subdivpolytriangles = 0;
1514         subdivpolyverts = 0;
1515         SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1516         if (subdivpolytriangles < 1)
1517                 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1518
1519         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1520         mesh->numverts = subdivpolyverts;
1521         mesh->numtriangles = subdivpolytriangles;
1522         mesh->vertex = (surfvertex_t *)(mesh + 1);
1523         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1524         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1525
1526         for (i = 0;i < mesh->numtriangles;i++)
1527                 for (j = 0;j < 3;j++)
1528                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1529
1530         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1531         {
1532                 VectorCopy(subdivpolyvert[i], v->v);
1533                 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1534                 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1535         }
1536 }
1537 #endif
1538
1539 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1540 {
1541         surfmesh_t *mesh;
1542         mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1543         mesh->numverts = numverts;
1544         mesh->numtriangles = numtriangles;
1545         mesh->vertex3f = (float *)(mesh + 1);
1546         mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1547         mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1548         mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1549         mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1550         mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1551         mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1552         mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1553         mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1554         mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1555         return mesh;
1556 }
1557
1558 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1559 {
1560         int i, lindex, j;
1561         float *vec, *vert, mins[3], maxs[3], val, *v;
1562         mtexinfo_t *tex;
1563
1564         // convert edges back to a normal polygon
1565         surf->poly_numverts = numedges;
1566         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1567         for (i = 0;i < numedges;i++)
1568         {
1569                 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1570                 if (lindex > 0)
1571                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1572                 else
1573                         vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1574                 VectorCopy(vec, vert);
1575                 vert += 3;
1576         }
1577
1578         // calculate polygon bounding box and center
1579         vert = surf->poly_verts;
1580         VectorCopy(vert, mins);
1581         VectorCopy(vert, maxs);
1582         vert += 3;
1583         for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1584         {
1585                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1586                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1587                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1588         }
1589         VectorCopy(mins, surf->poly_mins);
1590         VectorCopy(maxs, surf->poly_maxs);
1591         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1592         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1593         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1594
1595         // generate surface extents information
1596         tex = surf->texinfo;
1597         mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1598         mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1599         for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1600         {
1601                 for (j = 0;j < 2;j++)
1602                 {
1603                         val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1604                         if (mins[j] > val)
1605                                 mins[j] = val;
1606                         if (maxs[j] < val)
1607                                 maxs[j] = val;
1608                 }
1609         }
1610         for (i = 0;i < 2;i++)
1611         {
1612                 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1613                 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1614         }
1615 }
1616
1617 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1618 {
1619         dface_t *in;
1620         msurface_t *surf;
1621         int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1622         surfmesh_t *mesh;
1623         float s, t;
1624
1625         in = (void *)(mod_base + l->fileofs);
1626         if (l->filelen % sizeof(*in))
1627                 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1628         count = l->filelen / sizeof(*in);
1629         loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1630
1631         loadmodel->brushq1.numsurfaces = count;
1632         loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1633         loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1634         loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1635
1636         for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, in++, surf++)
1637         {
1638                 surf->number = surfnum;
1639                 // FIXME: validate edges, texinfo, etc?
1640                 firstedge = LittleLong(in->firstedge);
1641                 numedges = LittleShort(in->numedges);
1642                 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)
1643                         Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1644                 i = LittleShort(in->texinfo);
1645                 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1646                         Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1647                 surf->texinfo = loadmodel->brushq1.texinfo + i;
1648                 surf->flags = surf->texinfo->texture->flags;
1649
1650                 planenum = LittleShort(in->planenum);
1651                 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1652                         Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1653
1654                 if (LittleShort(in->side))
1655                         surf->flags |= SURF_PLANEBACK;
1656
1657                 surf->plane = loadmodel->brushq1.planes + planenum;
1658
1659                 // clear lightmap (filled in later)
1660                 surf->lightmaptexture = NULL;
1661
1662                 // force lightmap upload on first time seeing the surface
1663                 surf->cached_dlight = true;
1664
1665                 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1666
1667                 ssize = (surf->extents[0] >> 4) + 1;
1668                 tsize = (surf->extents[1] >> 4) + 1;
1669
1670                 // lighting info
1671                 for (i = 0;i < MAXLIGHTMAPS;i++)
1672                         surf->styles[i] = in->styles[i];
1673                 i = LittleLong(in->lightofs);
1674                 if (i == -1)
1675                         surf->samples = NULL;
1676                 else if (loadmodel->brushq1.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1677                         surf->samples = loadmodel->brushq1.lightdata + i;
1678                 else // LordHavoc: white lighting (bsp version 29)
1679                         surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1680
1681                 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1682                 {
1683                         if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1684                                 Host_Error("Bad surface extents");
1685                         // stainmap for permanent marks on walls
1686                         surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1687                         // clear to white
1688                         memset(surf->stainsamples, 255, ssize * tsize * 3);
1689                 }
1690         }
1691
1692         loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1693         loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1694
1695         for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++)
1696         {
1697                 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1698                 mesh->numverts = surf->poly_numverts;
1699                 mesh->numtriangles = surf->poly_numverts - 2;
1700                 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1701                 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1702                 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1703                 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1704                 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1705                 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1706                 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1707                 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1708                 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1709                 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1710
1711                 surf->lightmaptexturestride = 0;
1712                 surf->lightmaptexture = NULL;
1713
1714                 for (i = 0;i < mesh->numverts;i++)
1715                 {
1716                         mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1717                         mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1718                         mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1719                         s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1720                         t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1721                         mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1722                         mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1723                         mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1724                         mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1725                         mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1726                         mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1727                         mesh->lightmapoffsets[i] = 0;
1728                 }
1729
1730                 for (i = 0;i < mesh->numtriangles;i++)
1731                 {
1732                         mesh->element3i[i * 3 + 0] = 0;
1733                         mesh->element3i[i * 3 + 1] = i + 1;
1734                         mesh->element3i[i * 3 + 2] = i + 2;
1735                 }
1736
1737                 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1738                 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1739
1740                 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1741                 {
1742                         int i, iu, iv, smax, tmax;
1743                         float u, v, ubase, vbase, uscale, vscale;
1744
1745                         smax = surf->extents[0] >> 4;
1746                         tmax = surf->extents[1] >> 4;
1747
1748                         surf->flags |= SURF_LIGHTMAP;
1749                         if (r_miplightmaps.integer)
1750                         {
1751                                 surf->lightmaptexturestride = smax+1;
1752                                 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1753                         }
1754                         else
1755                         {
1756                                 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1757                                 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1758                         }
1759                         R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1760                         uscale = (uscale - ubase) / (smax + 1);
1761                         vscale = (vscale - vbase) / (tmax + 1);
1762
1763                         for (i = 0;i < mesh->numverts;i++)
1764                         {
1765                                 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1766                                 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1767                                 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1768                                 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1769                                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1770                                 iu = (int) u;
1771                                 iv = (int) v;
1772                                 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1773                         }
1774                 }
1775         }
1776 }
1777
1778 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1779 {
1780         node->parent = parent;
1781         if (node->contents < 0)
1782                 return;
1783         Mod_Q1BSP_SetParent(node->children[0], node);
1784         Mod_Q1BSP_SetParent(node->children[1], node);
1785 }
1786
1787 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1788 {
1789         int                     i, j, count, p;
1790         dnode_t         *in;
1791         mnode_t         *out;
1792
1793         in = (void *)(mod_base + l->fileofs);
1794         if (l->filelen % sizeof(*in))
1795                 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1796         count = l->filelen / sizeof(*in);
1797         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1798
1799         loadmodel->brushq1.nodes = out;
1800         loadmodel->brushq1.numnodes = count;
1801
1802         for ( i=0 ; i<count ; i++, in++, out++)
1803         {
1804                 for (j=0 ; j<3 ; j++)
1805                 {
1806                         out->mins[j] = LittleShort(in->mins[j]);
1807                         out->maxs[j] = LittleShort(in->maxs[j]);
1808                 }
1809
1810                 p = LittleLong(in->planenum);
1811                 out->plane = loadmodel->brushq1.planes + p;
1812
1813                 out->firstsurface = LittleShort(in->firstface);
1814                 out->numsurfaces = LittleShort(in->numfaces);
1815
1816                 for (j=0 ; j<2 ; j++)
1817                 {
1818                         p = LittleShort(in->children[j]);
1819                         if (p >= 0)
1820                                 out->children[j] = loadmodel->brushq1.nodes + p;
1821                         else
1822                                 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1823                 }
1824         }
1825
1826         Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL);    // sets nodes and leafs
1827 }
1828
1829 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1830 {
1831         dleaf_t         *in;
1832         mleaf_t         *out;
1833         int                     i, j, count, p;
1834
1835         in = (void *)(mod_base + l->fileofs);
1836         if (l->filelen % sizeof(*in))
1837                 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1838         count = l->filelen / sizeof(*in);
1839         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1840
1841         loadmodel->brushq1.leafs = out;
1842         loadmodel->brushq1.numleafs = count;
1843
1844         for ( i=0 ; i<count ; i++, in++, out++)
1845         {
1846                 for (j=0 ; j<3 ; j++)
1847                 {
1848                         out->mins[j] = LittleShort(in->mins[j]);
1849                         out->maxs[j] = LittleShort(in->maxs[j]);
1850                 }
1851
1852                 p = LittleLong(in->contents);
1853                 out->contents = p;
1854
1855                 out->firstmarksurface = loadmodel->brushq1.marksurfaces +
1856                         LittleShort(in->firstmarksurface);
1857                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1858
1859                 p = LittleLong(in->visofs);
1860                 if (p == -1)
1861                         out->compressed_vis = NULL;
1862                 else
1863                         out->compressed_vis = loadmodel->brushq1.visdata + p;
1864
1865                 for (j=0 ; j<4 ; j++)
1866                         out->ambient_sound_level[j] = in->ambient_level[j];
1867
1868                 // FIXME: Insert caustics here
1869         }
1870 }
1871
1872 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1873 {
1874         dclipnode_t *in, *out;
1875         int                     i, count;
1876         hull_t          *hull;
1877
1878         in = (void *)(mod_base + l->fileofs);
1879         if (l->filelen % sizeof(*in))
1880                 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1881         count = l->filelen / sizeof(*in);
1882         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1883
1884         loadmodel->brushq1.clipnodes = out;
1885         loadmodel->brushq1.numclipnodes = count;
1886
1887         if (loadmodel->brushq1.ishlbsp)
1888         {
1889                 hull = &loadmodel->brushq1.hulls[1];
1890                 hull->clipnodes = out;
1891                 hull->firstclipnode = 0;
1892                 hull->lastclipnode = count-1;
1893                 hull->planes = loadmodel->brushq1.planes;
1894                 hull->clip_mins[0] = -16;
1895                 hull->clip_mins[1] = -16;
1896                 hull->clip_mins[2] = -36;
1897                 hull->clip_maxs[0] = 16;
1898                 hull->clip_maxs[1] = 16;
1899                 hull->clip_maxs[2] = 36;
1900                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1901
1902                 hull = &loadmodel->brushq1.hulls[2];
1903                 hull->clipnodes = out;
1904                 hull->firstclipnode = 0;
1905                 hull->lastclipnode = count-1;
1906                 hull->planes = loadmodel->brushq1.planes;
1907                 hull->clip_mins[0] = -32;
1908                 hull->clip_mins[1] = -32;
1909                 hull->clip_mins[2] = -32;
1910                 hull->clip_maxs[0] = 32;
1911                 hull->clip_maxs[1] = 32;
1912                 hull->clip_maxs[2] = 32;
1913                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1914
1915                 hull = &loadmodel->brushq1.hulls[3];
1916                 hull->clipnodes = out;
1917                 hull->firstclipnode = 0;
1918                 hull->lastclipnode = count-1;
1919                 hull->planes = loadmodel->brushq1.planes;
1920                 hull->clip_mins[0] = -16;
1921                 hull->clip_mins[1] = -16;
1922                 hull->clip_mins[2] = -18;
1923                 hull->clip_maxs[0] = 16;
1924                 hull->clip_maxs[1] = 16;
1925                 hull->clip_maxs[2] = 18;
1926                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1927         }
1928         else
1929         {
1930                 hull = &loadmodel->brushq1.hulls[1];
1931                 hull->clipnodes = out;
1932                 hull->firstclipnode = 0;
1933                 hull->lastclipnode = count-1;
1934                 hull->planes = loadmodel->brushq1.planes;
1935                 hull->clip_mins[0] = -16;
1936                 hull->clip_mins[1] = -16;
1937                 hull->clip_mins[2] = -24;
1938                 hull->clip_maxs[0] = 16;
1939                 hull->clip_maxs[1] = 16;
1940                 hull->clip_maxs[2] = 32;
1941                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1942
1943                 hull = &loadmodel->brushq1.hulls[2];
1944                 hull->clipnodes = out;
1945                 hull->firstclipnode = 0;
1946                 hull->lastclipnode = count-1;
1947                 hull->planes = loadmodel->brushq1.planes;
1948                 hull->clip_mins[0] = -32;
1949                 hull->clip_mins[1] = -32;
1950                 hull->clip_mins[2] = -24;
1951                 hull->clip_maxs[0] = 32;
1952                 hull->clip_maxs[1] = 32;
1953                 hull->clip_maxs[2] = 64;
1954                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1955         }
1956
1957         for (i=0 ; i<count ; i++, out++, in++)
1958         {
1959                 out->planenum = LittleLong(in->planenum);
1960                 out->children[0] = LittleShort(in->children[0]);
1961                 out->children[1] = LittleShort(in->children[1]);
1962                 if (out->children[0] >= count || out->children[1] >= count)
1963                         Host_Error("Corrupt clipping hull(out of range child)\n");
1964         }
1965 }
1966
1967 //Duplicate the drawing hull structure as a clipping hull
1968 static void Mod_Q1BSP_MakeHull0(void)
1969 {
1970         mnode_t         *in;
1971         dclipnode_t *out;
1972         int                     i;
1973         hull_t          *hull;
1974
1975         hull = &loadmodel->brushq1.hulls[0];
1976
1977         in = loadmodel->brushq1.nodes;
1978         out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1979
1980         hull->clipnodes = out;
1981         hull->firstclipnode = 0;
1982         hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1983         hull->planes = loadmodel->brushq1.planes;
1984
1985         for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1986         {
1987                 out->planenum = in->plane - loadmodel->brushq1.planes;
1988                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1989                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1990         }
1991 }
1992
1993 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1994 {
1995         int i, j;
1996         short *in;
1997
1998         in = (void *)(mod_base + l->fileofs);
1999         if (l->filelen % sizeof(*in))
2000                 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2001         loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2002         loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2003
2004         for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2005         {
2006                 j = (unsigned) LittleShort(in[i]);
2007                 if (j >= loadmodel->brushq1.numsurfaces)
2008                         Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2009                 loadmodel->brushq1.marksurfaces[i] = j;
2010         }
2011 }
2012
2013 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2014 {
2015         int             i;
2016         int             *in;
2017
2018         in = (void *)(mod_base + l->fileofs);
2019         if (l->filelen % sizeof(*in))
2020                 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2021         loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2022         loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2023
2024         for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2025                 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2026 }
2027
2028
2029 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2030 {
2031         int                     i;
2032         mplane_t        *out;
2033         dplane_t        *in;
2034
2035         in = (void *)(mod_base + l->fileofs);
2036         if (l->filelen % sizeof(*in))
2037                 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2038
2039         loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2040         loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2041
2042         for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2043         {
2044                 out->normal[0] = LittleFloat(in->normal[0]);
2045                 out->normal[1] = LittleFloat(in->normal[1]);
2046                 out->normal[2] = LittleFloat(in->normal[2]);
2047                 out->dist = LittleFloat(in->dist);
2048
2049                 PlaneClassify(out);
2050         }
2051 }
2052
2053 #define MAX_POINTS_ON_WINDING 64
2054
2055 typedef struct
2056 {
2057         int numpoints;
2058         int padding;
2059         double points[8][3]; // variable sized
2060 }
2061 winding_t;
2062
2063 /*
2064 ==================
2065 NewWinding
2066 ==================
2067 */
2068 static winding_t *NewWinding(int points)
2069 {
2070         winding_t *w;
2071         int size;
2072
2073         if (points > MAX_POINTS_ON_WINDING)
2074                 Sys_Error("NewWinding: too many points\n");
2075
2076         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
2077         w = Mem_Alloc(loadmodel->mempool, size);
2078         memset(w, 0, size);
2079
2080         return w;
2081 }
2082
2083 static void FreeWinding(winding_t *w)
2084 {
2085         Mem_Free(w);
2086 }
2087
2088 /*
2089 =================
2090 BaseWindingForPlane
2091 =================
2092 */
2093 static winding_t *BaseWindingForPlane(mplane_t *p)
2094 {
2095         double org[3], vright[3], vup[3], normal[3];
2096         winding_t *w;
2097
2098         VectorCopy(p->normal, normal);
2099         VectorVectorsDouble(normal, vright, vup);
2100
2101         VectorScale(vup, 1024.0*1024.0*1024.0, vup);
2102         VectorScale(vright, 1024.0*1024.0*1024.0, vright);
2103
2104         // project a really big axis aligned box onto the plane
2105         w = NewWinding(4);
2106
2107         VectorScale(p->normal, p->dist, org);
2108
2109         VectorSubtract(org, vright, w->points[0]);
2110         VectorAdd(w->points[0], vup, w->points[0]);
2111
2112         VectorAdd(org, vright, w->points[1]);
2113         VectorAdd(w->points[1], vup, w->points[1]);
2114
2115         VectorAdd(org, vright, w->points[2]);
2116         VectorSubtract(w->points[2], vup, w->points[2]);
2117
2118         VectorSubtract(org, vright, w->points[3]);
2119         VectorSubtract(w->points[3], vup, w->points[3]);
2120
2121         w->numpoints = 4;
2122
2123         return w;
2124 }
2125
2126 /*
2127 ==================
2128 ClipWinding
2129
2130 Clips the winding to the plane, returning the new winding on the positive side
2131 Frees the input winding.
2132 If keepon is true, an exactly on-plane winding will be saved, otherwise
2133 it will be clipped away.
2134 ==================
2135 */
2136 static winding_t *ClipWinding(winding_t *in, mplane_t *split, int keepon)
2137 {
2138         double  dists[MAX_POINTS_ON_WINDING + 1];
2139         int             sides[MAX_POINTS_ON_WINDING + 1];
2140         int             counts[3];
2141         double  dot;
2142         int             i, j;
2143         double  *p1, *p2;
2144         double  mid[3];
2145         winding_t       *neww;
2146         int             maxpts;
2147
2148         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2149
2150         // determine sides for each point
2151         for (i = 0;i < in->numpoints;i++)
2152         {
2153                 dists[i] = dot = DotProduct(in->points[i], split->normal) - split->dist;
2154                 if (dot > ON_EPSILON)
2155                         sides[i] = SIDE_FRONT;
2156                 else if (dot < -ON_EPSILON)
2157                         sides[i] = SIDE_BACK;
2158                 else
2159                         sides[i] = SIDE_ON;
2160                 counts[sides[i]]++;
2161         }
2162         sides[i] = sides[0];
2163         dists[i] = dists[0];
2164
2165         if (keepon && !counts[0] && !counts[1])
2166                 return in;
2167
2168         if (!counts[0])
2169         {
2170                 FreeWinding(in);
2171                 return NULL;
2172         }
2173         if (!counts[1])
2174                 return in;
2175
2176         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2177         if (maxpts > MAX_POINTS_ON_WINDING)
2178                 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2179
2180         neww = NewWinding(maxpts);
2181
2182         for (i = 0;i < in->numpoints;i++)
2183         {
2184                 if (neww->numpoints >= maxpts)
2185                         Sys_Error("ClipWinding: points exceeded estimate");
2186
2187                 p1 = in->points[i];
2188
2189                 if (sides[i] == SIDE_ON)
2190                 {
2191                         VectorCopy(p1, neww->points[neww->numpoints]);
2192                         neww->numpoints++;
2193                         continue;
2194                 }
2195
2196                 if (sides[i] == SIDE_FRONT)
2197                 {
2198                         VectorCopy(p1, neww->points[neww->numpoints]);
2199                         neww->numpoints++;
2200                 }
2201
2202                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2203                         continue;
2204
2205                 // generate a split point
2206                 p2 = in->points[(i+1)%in->numpoints];
2207
2208                 dot = dists[i] / (dists[i]-dists[i+1]);
2209                 for (j = 0;j < 3;j++)
2210                 {       // avoid round off error when possible
2211                         if (split->normal[j] == 1)
2212                                 mid[j] = split->dist;
2213                         else if (split->normal[j] == -1)
2214                                 mid[j] = -split->dist;
2215                         else
2216                                 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2217                 }
2218
2219                 VectorCopy(mid, neww->points[neww->numpoints]);
2220                 neww->numpoints++;
2221         }
2222
2223         // free the original winding
2224         FreeWinding(in);
2225
2226         return neww;
2227 }
2228
2229
2230 /*
2231 ==================
2232 DivideWinding
2233
2234 Divides a winding by a plane, producing one or two windings.  The
2235 original winding is not damaged or freed.  If only on one side, the
2236 returned winding will be the input winding.  If on both sides, two
2237 new windings will be created.
2238 ==================
2239 */
2240 static void DivideWinding(winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2241 {
2242         double  dists[MAX_POINTS_ON_WINDING + 1];
2243         int             sides[MAX_POINTS_ON_WINDING + 1];
2244         int             counts[3];
2245         double  dot;
2246         int             i, j;
2247         double  *p1, *p2;
2248         double  mid[3];
2249         winding_t       *f, *b;
2250         int             maxpts;
2251
2252         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2253
2254         // determine sides for each point
2255         for (i = 0;i < in->numpoints;i++)
2256         {
2257                 dot = DotProduct(in->points[i], split->normal);
2258                 dot -= split->dist;
2259                 dists[i] = dot;
2260                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2261                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2262                 else sides[i] = SIDE_ON;
2263                 counts[sides[i]]++;
2264         }
2265         sides[i] = sides[0];
2266         dists[i] = dists[0];
2267
2268         *front = *back = NULL;
2269
2270         if (!counts[0])
2271         {
2272                 *back = in;
2273                 return;
2274         }
2275         if (!counts[1])
2276         {
2277                 *front = in;
2278                 return;
2279         }
2280
2281         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2282
2283         if (maxpts > MAX_POINTS_ON_WINDING)
2284                 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2285
2286         *front = f = NewWinding(maxpts);
2287         *back = b = NewWinding(maxpts);
2288
2289         for (i = 0;i < in->numpoints;i++)
2290         {
2291                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2292                         Sys_Error("DivideWinding: points exceeded estimate");
2293
2294                 p1 = in->points[i];
2295
2296                 if (sides[i] == SIDE_ON)
2297                 {
2298                         VectorCopy(p1, f->points[f->numpoints]);
2299                         f->numpoints++;
2300                         VectorCopy(p1, b->points[b->numpoints]);
2301                         b->numpoints++;
2302                         continue;
2303                 }
2304
2305                 if (sides[i] == SIDE_FRONT)
2306                 {
2307                         VectorCopy(p1, f->points[f->numpoints]);
2308                         f->numpoints++;
2309                 }
2310                 else if (sides[i] == SIDE_BACK)
2311                 {
2312                         VectorCopy(p1, b->points[b->numpoints]);
2313                         b->numpoints++;
2314                 }
2315
2316                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2317                         continue;
2318
2319                 // generate a split point
2320                 p2 = in->points[(i+1)%in->numpoints];
2321
2322                 dot = dists[i] / (dists[i]-dists[i+1]);
2323                 for (j = 0;j < 3;j++)
2324                 {       // avoid round off error when possible
2325                         if (split->normal[j] == 1)
2326                                 mid[j] = split->dist;
2327                         else if (split->normal[j] == -1)
2328                                 mid[j] = -split->dist;
2329                         else
2330                                 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2331                 }
2332
2333                 VectorCopy(mid, f->points[f->numpoints]);
2334                 f->numpoints++;
2335                 VectorCopy(mid, b->points[b->numpoints]);
2336                 b->numpoints++;
2337         }
2338 }
2339
2340 typedef struct portal_s
2341 {
2342         mplane_t plane;
2343         mnode_t *nodes[2];              // [0] = front side of plane
2344         struct portal_s *next[2];
2345         winding_t *winding;
2346         struct portal_s *chain; // all portals are linked into a list
2347 }
2348 portal_t;
2349
2350 static portal_t *portalchain;
2351
2352 /*
2353 ===========
2354 AllocPortal
2355 ===========
2356 */
2357 static portal_t *AllocPortal(void)
2358 {
2359         portal_t *p;
2360         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2361         p->chain = portalchain;
2362         portalchain = p;
2363         return p;
2364 }
2365
2366 static void FreePortal(portal_t *p)
2367 {
2368         Mem_Free(p);
2369 }
2370
2371 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2372 {
2373         // calculate children first
2374         if (node->children[0]->contents >= 0)
2375                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2376         if (node->children[1]->contents >= 0)
2377                 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2378
2379         // make combined bounding box from children
2380         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2381         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2382         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2383         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2384         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2385         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2386 }
2387
2388 static void Mod_Q1BSP_FinalizePortals(void)
2389 {
2390         int i, j, numportals, numpoints;
2391         portal_t *p, *pnext;
2392         mportal_t *portal;
2393         mvertex_t *point;
2394         mleaf_t *leaf, *endleaf;
2395         winding_t *w;
2396
2397         // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2398         leaf = loadmodel->brushq1.leafs;
2399         endleaf = leaf + loadmodel->brushq1.numleafs;
2400         for (;leaf < endleaf;leaf++)
2401         {
2402                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2403                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2404         }
2405         p = portalchain;
2406         while (p)
2407         {
2408                 if (p->winding)
2409                 {
2410                         for (i = 0;i < 2;i++)
2411                         {
2412                                 leaf = (mleaf_t *)p->nodes[i];
2413                                 w = p->winding;
2414                                 for (j = 0;j < w->numpoints;j++)
2415                                 {
2416                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2417                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2418                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2419                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2420                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2421                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2422                                 }
2423                         }
2424                 }
2425                 p = p->chain;
2426         }
2427
2428         Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2429
2430         // tally up portal and point counts
2431         p = portalchain;
2432         numportals = 0;
2433         numpoints = 0;
2434         while (p)
2435         {
2436                 // note: this check must match the one below or it will usually corrupt memory
2437                 // 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
2438                 if (p->winding && p->nodes[0] != p->nodes[1]
2439                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2440                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2441                 {
2442                         numportals += 2;
2443                         numpoints += p->winding->numpoints * 2;
2444                 }
2445                 p = p->chain;
2446         }
2447         loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2448         loadmodel->brushq1.numportals = numportals;
2449         loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2450         loadmodel->brushq1.numportalpoints = numpoints;
2451         // clear all leaf portal chains
2452         for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2453                 loadmodel->brushq1.leafs[i].portals = NULL;
2454         // process all portals in the global portal chain, while freeing them
2455         portal = loadmodel->brushq1.portals;
2456         point = loadmodel->brushq1.portalpoints;
2457         p = portalchain;
2458         portalchain = NULL;
2459         while (p)
2460         {
2461                 pnext = p->chain;
2462
2463                 if (p->winding)
2464                 {
2465                         // note: this check must match the one above or it will usually corrupt memory
2466                         // 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
2467                         if (p->nodes[0] != p->nodes[1]
2468                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2469                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2470                         {
2471                                 // first make the back to front portal(forward portal)
2472                                 portal->points = point;
2473                                 portal->numpoints = p->winding->numpoints;
2474                                 portal->plane.dist = p->plane.dist;
2475                                 VectorCopy(p->plane.normal, portal->plane.normal);
2476                                 portal->here = (mleaf_t *)p->nodes[1];
2477                                 portal->past = (mleaf_t *)p->nodes[0];
2478                                 // copy points
2479                                 for (j = 0;j < portal->numpoints;j++)
2480                                 {
2481                                         VectorCopy(p->winding->points[j], point->position);
2482                                         point++;
2483                                 }
2484                                 PlaneClassify(&portal->plane);
2485
2486                                 // link into leaf's portal chain
2487                                 portal->next = portal->here->portals;
2488                                 portal->here->portals = portal;
2489
2490                                 // advance to next portal
2491                                 portal++;
2492
2493                                 // then make the front to back portal(backward portal)
2494                                 portal->points = point;
2495                                 portal->numpoints = p->winding->numpoints;
2496                                 portal->plane.dist = -p->plane.dist;
2497                                 VectorNegate(p->plane.normal, portal->plane.normal);
2498                                 portal->here = (mleaf_t *)p->nodes[0];
2499                                 portal->past = (mleaf_t *)p->nodes[1];
2500                                 // copy points
2501                                 for (j = portal->numpoints - 1;j >= 0;j--)
2502                                 {
2503                                         VectorCopy(p->winding->points[j], point->position);
2504                                         point++;
2505                                 }
2506                                 PlaneClassify(&portal->plane);
2507
2508                                 // link into leaf's portal chain
2509                                 portal->next = portal->here->portals;
2510                                 portal->here->portals = portal;
2511
2512                                 // advance to next portal
2513                                 portal++;
2514                         }
2515                         FreeWinding(p->winding);
2516                 }
2517                 FreePortal(p);
2518                 p = pnext;
2519         }
2520 }
2521
2522 /*
2523 =============
2524 AddPortalToNodes
2525 =============
2526 */
2527 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2528 {
2529         if (!front)
2530                 Host_Error("AddPortalToNodes: NULL front node");
2531         if (!back)
2532                 Host_Error("AddPortalToNodes: NULL back node");
2533         if (p->nodes[0] || p->nodes[1])
2534                 Host_Error("AddPortalToNodes: already included");
2535         // 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
2536
2537         p->nodes[0] = front;
2538         p->next[0] = (portal_t *)front->portals;
2539         front->portals = (mportal_t *)p;
2540
2541         p->nodes[1] = back;
2542         p->next[1] = (portal_t *)back->portals;
2543         back->portals = (mportal_t *)p;
2544 }
2545
2546 /*
2547 =============
2548 RemovePortalFromNode
2549 =============
2550 */
2551 static void RemovePortalFromNodes(portal_t *portal)
2552 {
2553         int i;
2554         mnode_t *node;
2555         void **portalpointer;
2556         portal_t *t;
2557         for (i = 0;i < 2;i++)
2558         {
2559                 node = portal->nodes[i];
2560
2561                 portalpointer = (void **) &node->portals;
2562                 while (1)
2563                 {
2564                         t = *portalpointer;
2565                         if (!t)
2566                                 Host_Error("RemovePortalFromNodes: portal not in leaf");
2567
2568                         if (t == portal)
2569                         {
2570                                 if (portal->nodes[0] == node)
2571                                 {
2572                                         *portalpointer = portal->next[0];
2573                                         portal->nodes[0] = NULL;
2574                                 }
2575                                 else if (portal->nodes[1] == node)
2576                                 {
2577                                         *portalpointer = portal->next[1];
2578                                         portal->nodes[1] = NULL;
2579                                 }
2580                                 else
2581                                         Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2582                                 break;
2583                         }
2584
2585                         if (t->nodes[0] == node)
2586                                 portalpointer = (void **) &t->next[0];
2587                         else if (t->nodes[1] == node)
2588                                 portalpointer = (void **) &t->next[1];
2589                         else
2590                                 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2591                 }
2592         }
2593 }
2594
2595 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2596 {
2597         int side;
2598         mnode_t *front, *back, *other_node;
2599         mplane_t clipplane, *plane;
2600         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2601         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2602
2603         // if a leaf, we're done
2604         if (node->contents)
2605                 return;
2606
2607         plane = node->plane;
2608
2609         front = node->children[0];
2610         back = node->children[1];
2611         if (front == back)
2612                 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2613
2614         // create the new portal by generating a polygon for the node plane,
2615         // and clipping it by all of the other portals(which came from nodes above this one)
2616         nodeportal = AllocPortal();
2617         nodeportal->plane = *node->plane;
2618
2619         nodeportalwinding = BaseWindingForPlane(node->plane);
2620         side = 0;       // shut up compiler warning
2621         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2622         {
2623                 clipplane = portal->plane;
2624                 if (portal->nodes[0] == portal->nodes[1])
2625                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2626                 if (portal->nodes[0] == node)
2627                         side = 0;
2628                 else if (portal->nodes[1] == node)
2629                 {
2630                         clipplane.dist = -clipplane.dist;
2631                         VectorNegate(clipplane.normal, clipplane.normal);
2632                         side = 1;
2633                 }
2634                 else
2635                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2636
2637                 nodeportalwinding = ClipWinding(nodeportalwinding, &clipplane, true);
2638                 if (!nodeportalwinding)
2639                 {
2640                         Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2641                         break;
2642                 }
2643         }
2644
2645         if (nodeportalwinding)
2646         {
2647                 // if the plane was not clipped on all sides, there was an error
2648                 nodeportal->winding = nodeportalwinding;
2649                 AddPortalToNodes(nodeportal, front, back);
2650         }
2651
2652         // split the portals of this node along this node's plane and assign them to the children of this node
2653         // (migrating the portals downward through the tree)
2654         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2655         {
2656                 if (portal->nodes[0] == portal->nodes[1])
2657                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2658                 if (portal->nodes[0] == node)
2659                         side = 0;
2660                 else if (portal->nodes[1] == node)
2661                         side = 1;
2662                 else
2663                         Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2664                 nextportal = portal->next[side];
2665
2666                 other_node = portal->nodes[!side];
2667                 RemovePortalFromNodes(portal);
2668
2669                 // cut the portal into two portals, one on each side of the node plane
2670                 DivideWinding(portal->winding, plane, &frontwinding, &backwinding);
2671
2672                 if (!frontwinding)
2673                 {
2674                         if (side == 0)
2675                                 AddPortalToNodes(portal, back, other_node);
2676                         else
2677                                 AddPortalToNodes(portal, other_node, back);
2678                         continue;
2679                 }
2680                 if (!backwinding)
2681                 {
2682                         if (side == 0)
2683                                 AddPortalToNodes(portal, front, other_node);
2684                         else
2685                                 AddPortalToNodes(portal, other_node, front);
2686                         continue;
2687                 }
2688
2689                 // the winding is split
2690                 splitportal = AllocPortal();
2691                 temp = splitportal->chain;
2692                 *splitportal = *portal;
2693                 splitportal->chain = temp;
2694                 splitportal->winding = backwinding;
2695                 FreeWinding(portal->winding);
2696                 portal->winding = frontwinding;
2697
2698                 if (side == 0)
2699                 {
2700                         AddPortalToNodes(portal, front, other_node);
2701                         AddPortalToNodes(splitportal, back, other_node);
2702                 }
2703                 else
2704                 {
2705                         AddPortalToNodes(portal, other_node, front);
2706                         AddPortalToNodes(splitportal, other_node, back);
2707                 }
2708         }
2709
2710         Mod_Q1BSP_RecursiveNodePortals(front);
2711         Mod_Q1BSP_RecursiveNodePortals(back);
2712 }
2713
2714
2715 static void Mod_Q1BSP_MakePortals(void)
2716 {
2717         portalchain = NULL;
2718         Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2719         Mod_Q1BSP_FinalizePortals();
2720 }
2721
2722 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2723 {
2724 #if 0
2725         int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2726         msurface_t *surf, *s;
2727         float *v0, *v1, *v2, *v3;
2728         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2729                 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2730         for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2731         {
2732                 for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
2733                 {
2734                         if (surf->neighborsurfaces[vertnum])
2735                                 continue;
2736                         surf->neighborsurfaces[vertnum] = NULL;
2737                         for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2738                         {
2739                                 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2740                                  || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2741                                  || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2742                                  || s == surf)
2743                                         continue;
2744                                 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2745                                         if (s->neighborsurfaces[vnum] == surf)
2746                                                 break;
2747                                 if (vnum < s->poly_numverts)
2748                                         continue;
2749                                 for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
2750                                 {
2751                                         if (s->neighborsurfaces[vnum] == NULL
2752                                          && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2753                                           || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2754                                         {
2755                                                 surf->neighborsurfaces[vertnum] = s;
2756                                                 s->neighborsurfaces[vnum] = surf;
2757                                                 break;
2758                                         }
2759                                 }
2760                                 if (vnum < s->poly_numverts)
2761                                         break;
2762                         }
2763                 }
2764         }
2765 #endif
2766 }
2767
2768 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2769 {
2770         int i, j, stylecounts[256], totalcount, remapstyles[256];
2771         msurface_t *surf;
2772         memset(stylecounts, 0, sizeof(stylecounts));
2773         for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2774         {
2775                 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2776                 for (j = 0;j < MAXLIGHTMAPS;j++)
2777                         stylecounts[surf->styles[j]]++;
2778         }
2779         totalcount = 0;
2780         model->brushq1.light_styles = 0;
2781         for (i = 0;i < 255;i++)
2782         {
2783                 if (stylecounts[i])
2784                 {
2785                         remapstyles[i] = model->brushq1.light_styles++;
2786                         totalcount += stylecounts[i] + 1;
2787                 }
2788         }
2789         if (!totalcount)
2790                 return;
2791         model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2792         model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2793         model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2794         model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2795         model->brushq1.light_styles = 0;
2796         for (i = 0;i < 255;i++)
2797                 if (stylecounts[i])
2798                         model->brushq1.light_style[model->brushq1.light_styles++] = i;
2799         j = 0;
2800         for (i = 0;i < model->brushq1.light_styles;i++)
2801         {
2802                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2803                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2804         }
2805         for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2806         {
2807                 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2808                 for (j = 0;j < MAXLIGHTMAPS;j++)
2809                         if (surf->styles[j] != 255)
2810                                 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2811         }
2812         j = 0;
2813         for (i = 0;i < model->brushq1.light_styles;i++)
2814         {
2815                 *model->brushq1.light_styleupdatechains[i] = NULL;
2816                 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2817                 j += stylecounts[model->brushq1.light_style[i]] + 1;
2818         }
2819 }
2820
2821 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2822 {
2823         int i, j;
2824         for (i = 0;i < model->brushq1.numtextures;i++)
2825                 model->brushq1.pvstexturechainslength[i] = 0;
2826         for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2827         {
2828                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2829                 {
2830                         model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2831                         model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2832                 }
2833         }
2834         for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2835         {
2836                 if (model->brushq1.pvstexturechainslength[i])
2837                 {
2838                         model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2839                         j += model->brushq1.pvstexturechainslength[i] + 1;
2840                 }
2841                 else
2842                         model->brushq1.pvstexturechains[i] = NULL;
2843         }
2844         for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2845                 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2846                         *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2847         for (i = 0;i < model->brushq1.numtextures;i++)
2848         {
2849                 if (model->brushq1.pvstexturechainslength[i])
2850                 {
2851                         *model->brushq1.pvstexturechains[i] = NULL;
2852                         model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2853                 }
2854         }
2855 }
2856
2857 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2858 extern void R_Model_Brush_Draw(entity_render_t *ent);
2859 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2860 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
2861 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2862 {
2863         int i, j, k;
2864         dheader_t *header;
2865         dmodel_t *bm;
2866         mempool_t *mainmempool;
2867         char *loadname;
2868         model_t *originalloadmodel;
2869         float dist, modelyawradius, modelradius, *vec;
2870         msurface_t *surf;
2871         surfmesh_t *mesh;
2872
2873         mod->type = mod_brush;
2874
2875         header = (dheader_t *)buffer;
2876
2877         i = LittleLong(header->version);
2878         if (i != BSPVERSION && i != 30)
2879                 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2880         mod->brushq1.ishlbsp = i == 30;
2881
2882         mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2883         mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
2884         mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2885         mod->brushq1.LeafPVS = Mod_Q1BSP_LeafPVS;
2886         mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2887
2888         if (loadmodel->isworldmodel)
2889         {
2890                 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
2891                 // until we get a texture for it...
2892                 R_ResetQuakeSky();
2893         }
2894
2895 // swap all the lumps
2896         mod_base = (qbyte *)header;
2897
2898         for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2899                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2900
2901 // load into heap
2902
2903         // store which lightmap format to use
2904         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2905
2906         Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2907         Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2908         Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2909         Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2910         Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2911         Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2912         Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2913         Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2914         Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2915         Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2916         Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2917         Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2918         Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2919         Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2920         Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2921
2922         Mod_Q1BSP_MakeHull0();
2923         Mod_Q1BSP_MakePortals();
2924
2925         mod->numframes = 2;             // regular and alternate animation
2926
2927         mainmempool = mod->mempool;
2928         loadname = mod->name;
2929
2930         Mod_Q1BSP_LoadLightList();
2931         originalloadmodel = loadmodel;
2932
2933 //
2934 // set up the submodels(FIXME: this is confusing)
2935 //
2936         for (i = 0;i < mod->brushq1.numsubmodels;i++)
2937         {
2938                 bm = &mod->brushq1.submodels[i];
2939
2940                 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2941                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2942                 {
2943                         mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2944                         mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2945                 }
2946
2947                 mod->brushq1.firstmodelsurface = bm->firstface;
2948                 mod->brushq1.nummodelsurfaces = bm->numfaces;
2949
2950                 // this gets altered below if sky is used
2951                 mod->DrawSky = NULL;
2952                 mod->Draw = R_Model_Brush_Draw;
2953                 mod->DrawFakeShadow = NULL;
2954                 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2955                 mod->DrawLight = R_Model_Brush_DrawLight;
2956                 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2957                 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2958                 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2959                 Mod_Q1BSP_BuildPVSTextureChains(mod);
2960                 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2961                 if (mod->brushq1.nummodelsurfaces)
2962                 {
2963                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2964                         mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2965                         mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2966                         modelyawradius = 0;
2967                         modelradius = 0;
2968                         for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2969                         {
2970                                 // we only need to have a drawsky function if it is used(usually only on world model)
2971                                 if (surf->texinfo->texture->shader == &Cshader_sky)
2972                                         mod->DrawSky = R_Model_Brush_DrawSky;
2973                                 // LordHavoc: submodels always clip, even if water
2974                                 if (mod->brushq1.numsubmodels - 1)
2975                                         surf->flags |= SURF_SOLIDCLIP;
2976                                 // calculate bounding shapes
2977                                 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2978                                 {
2979                                         for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2980                                         {
2981                                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2982                                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2983                                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2984                                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2985                                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2986                                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2987                                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2988                                                 if (modelyawradius < dist)
2989                                                         modelyawradius = dist;
2990                                                 dist += vec[2]*vec[2];
2991                                                 if (modelradius < dist)
2992                                                         modelradius = dist;
2993                                         }
2994                                 }
2995                         }
2996                         modelyawradius = sqrt(modelyawradius);
2997                         modelradius = sqrt(modelradius);
2998                         mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2999                         mod->yawmins[2] = mod->normalmins[2];
3000                         mod->yawmaxs[2] = mod->normalmaxs[2];
3001                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3002                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3003                         mod->radius = modelradius;
3004                         mod->radius2 = modelradius * modelradius;
3005                 }
3006                 else
3007                 {
3008                         // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3009                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
3010                 }
3011                 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
3012
3013                 mod->brushq1.numleafs = bm->visleafs;
3014
3015                 // LordHavoc: only register submodels if it is the world
3016                 // (prevents bsp models from replacing world submodels)
3017                 if (loadmodel->isworldmodel && i < (mod->brushq1.numsubmodels - 1))
3018                 {
3019                         char    name[10];
3020                         // duplicate the basic information
3021                         sprintf(name, "*%i", i+1);
3022                         loadmodel = Mod_FindName(name);
3023                         *loadmodel = *mod;
3024                         strcpy(loadmodel->name, name);
3025                         // textures and memory belong to the main model
3026                         loadmodel->texturepool = NULL;
3027                         loadmodel->mempool = NULL;
3028                         mod = loadmodel;
3029                 }
3030         }
3031
3032         loadmodel = originalloadmodel;
3033         //Mod_Q1BSP_ProcessLightList();
3034 }
3035
3036 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3037 {
3038 }
3039
3040 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3041 {
3042 /*
3043         d_t *in;
3044         m_t *out;
3045         int i, count;
3046
3047         in = (void *)(mod_base + l->fileofs);
3048         if (l->filelen % sizeof(*in))
3049                 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3050         count = l->filelen / sizeof(*in);
3051         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3052
3053         loadmodel-> = out;
3054         loadmodel->num = count;
3055
3056         for (i = 0;i < count;i++, in++, out++)
3057         {
3058         }
3059 */
3060 }
3061
3062 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3063 {
3064 /*
3065         d_t *in;
3066         m_t *out;
3067         int i, count;
3068
3069         in = (void *)(mod_base + l->fileofs);
3070         if (l->filelen % sizeof(*in))
3071                 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3072         count = l->filelen / sizeof(*in);
3073         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3074
3075         loadmodel-> = out;
3076         loadmodel->num = count;
3077
3078         for (i = 0;i < count;i++, in++, out++)
3079         {
3080         }
3081 */
3082 }
3083
3084 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3085 {
3086 /*
3087         d_t *in;
3088         m_t *out;
3089         int i, count;
3090
3091         in = (void *)(mod_base + l->fileofs);
3092         if (l->filelen % sizeof(*in))
3093                 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3094         count = l->filelen / sizeof(*in);
3095         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3096
3097         loadmodel-> = out;
3098         loadmodel->num = count;
3099
3100         for (i = 0;i < count;i++, in++, out++)
3101         {
3102         }
3103 */
3104 }
3105
3106 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3107 {
3108 /*
3109         d_t *in;
3110         m_t *out;
3111         int i, count;
3112
3113         in = (void *)(mod_base + l->fileofs);
3114         if (l->filelen % sizeof(*in))
3115                 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3116         count = l->filelen / sizeof(*in);
3117         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3118
3119         loadmodel-> = out;
3120         loadmodel->num = count;
3121
3122         for (i = 0;i < count;i++, in++, out++)
3123         {
3124         }
3125 */
3126 }
3127
3128 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3129 {
3130 /*
3131         d_t *in;
3132         m_t *out;
3133         int i, count;
3134
3135         in = (void *)(mod_base + l->fileofs);
3136         if (l->filelen % sizeof(*in))
3137                 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3138         count = l->filelen / sizeof(*in);
3139         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3140
3141         loadmodel-> = out;
3142         loadmodel->num = count;
3143
3144         for (i = 0;i < count;i++, in++, out++)
3145         {
3146         }
3147 */
3148 }
3149
3150 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3151 {
3152 /*
3153         d_t *in;
3154         m_t *out;
3155         int i, count;
3156
3157         in = (void *)(mod_base + l->fileofs);
3158         if (l->filelen % sizeof(*in))
3159                 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3160         count = l->filelen / sizeof(*in);
3161         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3162
3163         loadmodel-> = out;
3164         loadmodel->num = count;
3165
3166         for (i = 0;i < count;i++, in++, out++)
3167         {
3168         }
3169 */
3170 }
3171
3172 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3173 {
3174 /*
3175         d_t *in;
3176         m_t *out;
3177         int i, count;
3178
3179         in = (void *)(mod_base + l->fileofs);
3180         if (l->filelen % sizeof(*in))
3181                 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3182         count = l->filelen / sizeof(*in);
3183         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3184
3185         loadmodel-> = out;
3186         loadmodel->num = count;
3187
3188         for (i = 0;i < count;i++, in++, out++)
3189         {
3190         }
3191 */
3192 }
3193
3194 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3195 {
3196 /*
3197         d_t *in;
3198         m_t *out;
3199         int i, count;
3200
3201         in = (void *)(mod_base + l->fileofs);
3202         if (l->filelen % sizeof(*in))
3203                 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3204         count = l->filelen / sizeof(*in);
3205         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3206
3207         loadmodel-> = out;
3208         loadmodel->num = count;
3209
3210         for (i = 0;i < count;i++, in++, out++)
3211         {
3212         }
3213 */
3214 }
3215
3216 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3217 {
3218 /*
3219         d_t *in;
3220         m_t *out;
3221         int i, count;
3222
3223         in = (void *)(mod_base + l->fileofs);
3224         if (l->filelen % sizeof(*in))
3225                 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3226         count = l->filelen / sizeof(*in);
3227         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3228
3229         loadmodel-> = out;
3230         loadmodel->num = count;
3231
3232         for (i = 0;i < count;i++, in++, out++)
3233         {
3234         }
3235 */
3236 }
3237
3238 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3239 {
3240 /*
3241         d_t *in;
3242         m_t *out;
3243         int i, count;
3244
3245         in = (void *)(mod_base + l->fileofs);
3246         if (l->filelen % sizeof(*in))
3247                 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3248         count = l->filelen / sizeof(*in);
3249         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3250
3251         loadmodel-> = out;
3252         loadmodel->num = count;
3253
3254         for (i = 0;i < count;i++, in++, out++)
3255         {
3256         }
3257 */
3258 }
3259
3260 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3261 {
3262 /*
3263         d_t *in;
3264         m_t *out;
3265         int i, count;
3266
3267         in = (void *)(mod_base + l->fileofs);
3268         if (l->filelen % sizeof(*in))
3269                 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3270         count = l->filelen / sizeof(*in);
3271         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3272
3273         loadmodel-> = out;
3274         loadmodel->num = count;
3275
3276         for (i = 0;i < count;i++, in++, out++)
3277         {
3278         }
3279 */
3280 }
3281
3282 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3283 {
3284 /*
3285         d_t *in;
3286         m_t *out;
3287         int i, count;
3288
3289         in = (void *)(mod_base + l->fileofs);
3290         if (l->filelen % sizeof(*in))
3291                 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3292         count = l->filelen / sizeof(*in);
3293         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3294
3295         loadmodel-> = out;
3296         loadmodel->num = count;
3297
3298         for (i = 0;i < count;i++, in++, out++)
3299         {
3300         }
3301 */
3302 }
3303
3304 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3305 {
3306 /*
3307         d_t *in;
3308         m_t *out;
3309         int i, count;
3310
3311         in = (void *)(mod_base + l->fileofs);
3312         if (l->filelen % sizeof(*in))
3313                 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3314         count = l->filelen / sizeof(*in);
3315         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3316
3317         loadmodel-> = out;
3318         loadmodel->num = count;
3319
3320         for (i = 0;i < count;i++, in++, out++)
3321         {
3322         }
3323 */
3324 }
3325
3326 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3327 {
3328 /*
3329         d_t *in;
3330         m_t *out;
3331         int i, count;
3332
3333         in = (void *)(mod_base + l->fileofs);
3334         if (l->filelen % sizeof(*in))
3335                 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3336         count = l->filelen / sizeof(*in);
3337         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3338
3339         loadmodel-> = out;
3340         loadmodel->num = count;
3341
3342         for (i = 0;i < count;i++, in++, out++)
3343         {
3344         }
3345 */
3346 }
3347
3348 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3349 {
3350 /*
3351         d_t *in;
3352         m_t *out;
3353         int i, count;
3354
3355         in = (void *)(mod_base + l->fileofs);
3356         if (l->filelen % sizeof(*in))
3357                 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3358         count = l->filelen / sizeof(*in);
3359         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3360
3361         loadmodel-> = out;
3362         loadmodel->num = count;
3363
3364         for (i = 0;i < count;i++, in++, out++)
3365         {
3366         }
3367 */
3368 }
3369
3370 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3371 {
3372 /*
3373         d_t *in;
3374         m_t *out;
3375         int i, count;
3376
3377         in = (void *)(mod_base + l->fileofs);
3378         if (l->filelen % sizeof(*in))
3379                 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3380         count = l->filelen / sizeof(*in);
3381         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3382
3383         loadmodel-> = out;
3384         loadmodel->num = count;
3385
3386         for (i = 0;i < count;i++, in++, out++)
3387         {
3388         }
3389 */
3390 }
3391
3392 static void Mod_Q2BSP_LoadModels(lump_t *l)
3393 {
3394 /*
3395         d_t *in;
3396         m_t *out;
3397         int i, count;
3398
3399         in = (void *)(mod_base + l->fileofs);
3400         if (l->filelen % sizeof(*in))
3401                 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3402         count = l->filelen / sizeof(*in);
3403         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3404
3405         loadmodel-> = out;
3406         loadmodel->num = count;
3407
3408         for (i = 0;i < count;i++, in++, out++)
3409         {
3410         }
3411 */
3412 }
3413
3414 void Mod_Q2BSP_Load(model_t *mod, void *buffer)
3415 {
3416         int i;
3417         q2dheader_t *header;
3418
3419         Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3420
3421         mod->type = mod_brushq2;
3422
3423         header = (q2dheader_t *)buffer;
3424
3425         i = LittleLong(header->version);
3426         if (i != Q2BSPVERSION)
3427                 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3428         mod->brushq1.ishlbsp = false;
3429         if (loadmodel->isworldmodel)
3430         {
3431                 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
3432                 // until we get a texture for it...
3433                 R_ResetQuakeSky();
3434         }
3435
3436         mod_base = (qbyte *)header;
3437
3438         // swap all the lumps
3439         for (i = 0;i < (int) sizeof(*header) / 4;i++)
3440                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3441
3442         // store which lightmap format to use
3443         mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3444
3445         Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3446         Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3447         Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3448         Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3449         Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3450         Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3451         Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3452         Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3453         Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3454         Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3455         Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3456         Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3457         Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3458         Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3459         Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3460         Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3461         Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3462         // LordHavoc: must go last because this makes the submodels
3463         Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3464 }
3465
3466
3467 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3468 {
3469         const char *data;
3470         char key[128], value[4096];
3471         float v[3];
3472         loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3473         loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3474         loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3475         if (!l->filelen)
3476                 return;
3477         loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3478         memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3479         data = loadmodel->brush.entities;
3480         // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3481         if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3482         {
3483                 while (1)
3484                 {
3485                         if (!COM_ParseToken(&data, false))
3486                                 break; // error
3487                         if (com_token[0] == '}')
3488                                 break; // end of worldspawn
3489                         if (com_token[0] == '_')
3490                                 strcpy(key, com_token + 1);
3491                         else
3492                                 strcpy(key, com_token);
3493                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3494                                 key[strlen(key)-1] = 0;
3495                         if (!COM_ParseToken(&data, false))
3496                                 break; // error
3497                         strcpy(value, com_token);
3498                         if (!strcmp("gridsize", key))
3499                         {
3500                                 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3501                                         VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3502                         }
3503                 }
3504         }
3505 }
3506
3507 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3508 {
3509         q3dtexture_t *in;
3510         q3mtexture_t *out;
3511         int i, count;
3512
3513         in = (void *)(mod_base + l->fileofs);
3514         if (l->filelen % sizeof(*in))
3515                 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3516         count = l->filelen / sizeof(*in);
3517         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3518
3519         loadmodel->brushq3.data_textures = out;
3520         loadmodel->brushq3.num_textures = count;
3521
3522         for (i = 0;i < count;i++, in++, out++)
3523         {
3524                 strncpy(out->name, in->name, sizeof(out->name) - 1);
3525                 out->surfaceflags = LittleLong(in->surfaceflags);
3526                 out->contents = LittleLong(in->contents);
3527
3528                 out->number = i;
3529                 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3530         }
3531 }
3532
3533 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3534 {
3535         q3dplane_t *in;
3536         mplane_t *out;
3537         int i, count;
3538
3539         in = (void *)(mod_base + l->fileofs);
3540         if (l->filelen % sizeof(*in))
3541                 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3542         count = l->filelen / sizeof(*in);
3543         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3544
3545         loadmodel->brushq3.data_planes = out;
3546         loadmodel->brushq3.num_planes = count;
3547
3548         for (i = 0;i < count;i++, in++, out++)
3549         {
3550                 out->normal[0] = LittleLong(in->normal[0]);
3551                 out->normal[1] = LittleLong(in->normal[1]);
3552                 out->normal[2] = LittleLong(in->normal[2]);
3553                 out->dist = LittleLong(in->dist);
3554                 PlaneClassify(out);
3555         }
3556 }
3557
3558 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3559 {
3560         q3dbrushside_t *in;
3561         q3mbrushside_t *out;
3562         int i, n, count;
3563
3564         in = (void *)(mod_base + l->fileofs);
3565         if (l->filelen % sizeof(*in))
3566                 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3567         count = l->filelen / sizeof(*in);
3568         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3569
3570         loadmodel->brushq3.data_brushsides = out;
3571         loadmodel->brushq3.num_brushsides = count;
3572
3573         for (i = 0;i < count;i++, in++, out++)
3574         {
3575                 n = LittleLong(in->planeindex);
3576                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3577                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3578                 out->plane = loadmodel->brushq3.data_planes + n;
3579                 n = LittleLong(in->textureindex);
3580                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3581                         Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3582                 out->texture = loadmodel->brushq3.data_textures + n;
3583         }
3584 }
3585
3586 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3587 {
3588         q3dbrush_t *in;
3589         q3mbrush_t *out;
3590         int i, j, k, m, n, c, count, numpoints, numplanes;
3591         winding_t *w;
3592         colpointf_t pointsbuf[256*3];
3593         colplanef_t planesbuf[256], colplanef;
3594
3595         in = (void *)(mod_base + l->fileofs);
3596         if (l->filelen % sizeof(*in))
3597                 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3598         count = l->filelen / sizeof(*in);
3599         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3600
3601         loadmodel->brushq3.data_brushes = out;
3602         loadmodel->brushq3.num_brushes = count;
3603
3604         for (i = 0;i < count;i++, in++, out++)
3605         {
3606                 n = LittleLong(in->firstbrushside);
3607                 c = LittleLong(in->numbrushsides);
3608                 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3609                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3610                 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3611                 out->numbrushsides = c;
3612                 n = LittleLong(in->textureindex);
3613                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3614                         Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3615                 out->texture = loadmodel->brushq3.data_textures + n;
3616
3617                 // construct a collision brush, which needs points and planes...
3618                 // each point and plane should be unique, and they don't refer to
3619                 // eachother in any way, so keeping them unique is fairly easy
3620                 numpoints = 0;
3621                 numplanes = 0;
3622                 for (j = 0;j < out->numbrushsides;j++)
3623                 {
3624                         // create a huge polygon for the plane
3625                         w = BaseWindingForPlane(out->firstbrushside[j].plane);
3626                         // clip it by all other planes
3627                         for (k = 0;k < out->numbrushsides && w;k++)
3628                                 if (k != j)
3629                                         w = ClipWinding(w, out->firstbrushside[k].plane, true);
3630                         // if nothing is left, skip it
3631                         // FIXME: should keep count of how many were skipped and report
3632                         // it, just for sake of statistics
3633                         if (!w)
3634                                 continue;
3635                         // add the points uniquely (no duplicates)
3636                         for (k = 0;k < w->numpoints;k++)
3637                         {
3638                                 for (m = 0;m < numpoints;m++)
3639                                         if (VectorDistance2(w->points[k * 3], pointsbuf[m * 3].v) < DIST_EPSILON)
3640                                                 break;
3641                                 if (m == numpoints)
3642                                 {
3643                                         // check if there are too many and skip the brush
3644                                         if (numpoints >= 256)
3645                                         {
3646                                                 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many points for buffer\n");
3647                                                 FreeWinding(w);
3648                                                 goto failedtomakecolbrush;
3649                                         }
3650                                         // add the new one
3651                                         VectorCopy(w->points[k * 3], pointsbuf[numpoints * 3].v);
3652                                         numpoints++;
3653                                 }
3654                         }
3655                         // add the plane uniquely (no duplicates)
3656                         memset(&colplanef, 0, sizeof(colplanef));
3657                         VectorCopy(out->firstbrushside[k].plane->normal, colplanef.normal);
3658                         colplanef.dist = out->firstbrushside[k].plane->dist;
3659                         for (k = 0;k < numplanes;k++)
3660                                 if (VectorCompare(planesbuf[k].normal, colplanef.normal) && planesbuf[k].dist == colplanef.dist)
3661                                         break;
3662                         if (k == numplanes)
3663                         {
3664                                 // check if there are too many and skip the brush
3665                                 if (numplanes >= 256)
3666                                 {
3667                                         Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many planes for buffer\n");
3668                                         FreeWinding(w);
3669                                         goto failedtomakecolbrush;
3670                                 }
3671                                 // add the new one
3672                                 planesbuf[numplanes++] = colplanef;
3673                         }
3674                         FreeWinding(w);
3675                 }
3676                 // if anything is left, create the collision brush
3677                 if (numplanes && numpoints)
3678                 {
3679                         out->colbrushf = Collision_AllocBrushFloat(loadmodel->mempool, numpoints, numplanes);
3680                         memcpy(out->colbrushf->points, pointsbuf, numpoints * sizeof(float[3]));
3681                         memcpy(out->colbrushf->planes, planesbuf, numplanes * sizeof(mplane_t));
3682                 }
3683                 // return from errors to here
3684                 failedtomakecolbrush:;
3685         }
3686 }
3687
3688 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3689 {
3690         q3deffect_t *in;
3691         q3meffect_t *out;
3692         int i, n, count;
3693
3694         in = (void *)(mod_base + l->fileofs);
3695         if (l->filelen % sizeof(*in))
3696                 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3697         count = l->filelen / sizeof(*in);
3698         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3699
3700         loadmodel->brushq3.data_effects = out;
3701         loadmodel->brushq3.num_effects = count;
3702
3703         for (i = 0;i < count;i++, in++, out++)
3704         {
3705                 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3706                 n = LittleLong(in->brushindex);
3707                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3708                         Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3709                 out->brush = loadmodel->brushq3.data_brushes + n;
3710                 out->unknown = LittleLong(in->unknown);
3711         }
3712 }
3713
3714 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3715 {
3716         q3dvertex_t *in;
3717         int i, count;
3718
3719         in = (void *)(mod_base + l->fileofs);
3720         if (l->filelen % sizeof(*in))
3721                 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3722         loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3723         loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3724         loadmodel->brushq3.data_texturetexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3725         loadmodel->brushq3.data_lightmaptexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3726         loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3727         loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3728         loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3729         loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3730
3731         for (i = 0;i < count;i++, in++)
3732         {
3733                 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3734                 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3735                 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3736                 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3737                 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3738                 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3739                 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3740                 // svector/tvector are calculated later in face loading
3741                 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3742                 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3743                 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3744                 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3745                 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3746                 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3747                 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3748                 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3749                 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3750                 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3751                 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3752                 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3753                 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3754         }
3755 }
3756
3757 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3758 {
3759         int *in;
3760         int *out;
3761         int i, count;
3762
3763         in = (void *)(mod_base + l->fileofs);
3764         if (l->filelen % sizeof(int[3]))
3765                 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3766         count = l->filelen / sizeof(*in);
3767         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3768
3769         loadmodel->brushq3.num_triangles = count / 3;
3770         loadmodel->brushq3.data_element3i = out;
3771         loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3772
3773         for (i = 0;i < count;i++, in++, out++)
3774         {
3775                 *out = LittleLong(*in);
3776                 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3777                         Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices);
3778         }
3779 }
3780
3781 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3782 {
3783         q3dlightmap_t *in;
3784         rtexture_t **out;
3785         int i, count;
3786
3787         in = (void *)(mod_base + l->fileofs);
3788         if (l->filelen % sizeof(*in))
3789                 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3790         count = l->filelen / sizeof(*in);
3791         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3792
3793         loadmodel->brushq3.data_lightmaps = out;
3794         loadmodel->brushq3.num_lightmaps = count;
3795
3796         for (i = 0;i < count;i++, in++, out++)
3797                 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3798 }
3799
3800 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3801 {
3802         q3dface_t *in;
3803         q3mface_t *out;
3804         int i, j, n, count, invalidelements, patchsize[2];
3805
3806         in = (void *)(mod_base + l->fileofs);
3807         if (l->filelen % sizeof(*in))
3808                 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3809         count = l->filelen / sizeof(*in);
3810         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3811
3812         loadmodel->brushq3.data_faces = out;
3813         loadmodel->brushq3.num_faces = count;
3814
3815         for (i = 0;i < count;i++, in++, out++)
3816         {
3817                 // check face type first
3818                 out->type = LittleLong(in->type);
3819                 if (out->type != Q3FACETYPE_POLYGON
3820                  && out->type != Q3FACETYPE_PATCH
3821                  && out->type != Q3FACETYPE_MESH
3822                  && out->type != Q3FACETYPE_FLARE)
3823                 {
3824                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3825                         out->type = 0; // error
3826                         continue;
3827                 }
3828
3829                 n = LittleLong(in->textureindex);
3830                 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3831                 {
3832                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3833                         out->type = 0; // error
3834                         continue;
3835                         n = 0;
3836                 }
3837                 out->texture = loadmodel->brushq3.data_textures + n;
3838                 n = LittleLong(in->effectindex);
3839                 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3840                 {
3841                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3842                         n = -1;
3843                 }
3844                 if (n == -1)
3845                         out->effect = NULL;
3846                 else
3847                         out->effect = loadmodel->brushq3.data_effects + n;
3848                 n = LittleLong(in->lightmapindex);
3849                 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3850                 {
3851                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3852                         n = -1;
3853                 }
3854                 if (n == -1)
3855                         out->lightmaptexture = NULL;
3856                 else
3857                         out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3858
3859                 out->firstvertex = LittleLong(in->firstvertex);
3860                 out->numvertices = LittleLong(in->numvertices);
3861                 out->firstelement = LittleLong(in->firstelement);
3862                 out->numelements = LittleLong(in->numelements);
3863                 out->numtriangles = out->numelements / 3;
3864                 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3865                 {
3866                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->numvertices, loadmodel->brushq3.num_vertices);
3867                         out->type = 0; // error
3868                         continue;
3869                 }
3870                 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3871                 {
3872                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->numelements, loadmodel->brushq3.num_triangles * 3);
3873                         out->type = 0; // error
3874                         continue;
3875                 }
3876                 if (out->numtriangles * 3 != out->numelements)
3877                 {
3878                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3879                         out->type = 0; // error
3880                         continue;
3881                 }
3882                 switch(out->type)
3883                 {
3884                 case Q3FACETYPE_POLYGON:
3885                 case Q3FACETYPE_MESH:
3886                         out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3887                         out->data_texturetexcoord2f = loadmodel->brushq3.data_texturetexcoord2f + out->firstvertex * 2;
3888                         out->data_lightmaptexcoord2f = loadmodel->brushq3.data_lightmaptexcoord2f + out->firstvertex * 2;
3889                         out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3890                         out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3891                         out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3892                         out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3893                         out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3894                         out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3895                         break;
3896                 case Q3FACETYPE_PATCH:
3897                         patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3898                         patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3899                         if (patchsize[0] < 1 || patchsize[1] < 1)
3900                         {
3901                                 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3902                                 out->type = 0; // error
3903                                 continue;
3904                         }
3905                         // FIXME: convert patch to triangles here!
3906                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
3907                         out->type = 0;
3908                         continue;
3909                         break;
3910                 case Q3FACETYPE_FLARE:
3911                         Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3912                         out->type = 0;
3913                         continue;
3914                         break;
3915                 }
3916                 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3917                         if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3918                                 invalidelements++;
3919                 if (invalidelements)
3920                 {
3921                         Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->contents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->contents, out->firstvertex, out->numvertices, out->firstelement, out->numelements);
3922                         for (j = 0;j < out->numelements;j++)
3923                         {
3924                                 Con_Printf(" %i", out->data_element3i[j]);
3925                                 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3926                                         out->data_element3i[j] = 0;
3927                         }
3928                         Con_Printf("\n");
3929                 }
3930         }
3931 }
3932
3933 static void Mod_Q3BSP_LoadModels(lump_t *l)
3934 {
3935         q3dmodel_t *in;
3936         q3mmodel_t *out;
3937         int i, j, n, c, count;
3938
3939         in = (void *)(mod_base + l->fileofs);
3940         if (l->filelen % sizeof(*in))
3941                 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3942         count = l->filelen / sizeof(*in);
3943         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3944
3945         loadmodel->brushq3.data_models = out;
3946         loadmodel->brushq3.num_models = count;
3947
3948         for (i = 0;i < count;i++, in++, out++)
3949         {
3950                 for (j = 0;j < 3;j++)
3951                 {
3952                         out->mins[j] = LittleFloat(in->mins[j]);
3953                         out->maxs[j] = LittleFloat(in->maxs[j]);
3954                 }
3955                 n = LittleLong(in->firstface);
3956                 c = LittleLong(in->numfaces);
3957                 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3958                         Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3959                 out->firstface = loadmodel->brushq3.data_faces + n;
3960                 out->numfaces = c;
3961                 n = LittleLong(in->firstbrush);
3962                 c = LittleLong(in->numbrushes);
3963                 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3964                         Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3965                 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3966                 out->numbrushes = c;
3967         }
3968 }
3969
3970 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3971 {
3972         int *in;
3973         q3mbrush_t **out;
3974         int i, n, count;
3975
3976         in = (void *)(mod_base + l->fileofs);
3977         if (l->filelen % sizeof(*in))
3978                 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3979         count = l->filelen / sizeof(*in);
3980         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3981
3982         loadmodel->brushq3.data_leafbrushes = out;
3983         loadmodel->brushq3.num_leafbrushes = count;
3984
3985         for (i = 0;i < count;i++, in++, out++)
3986         {
3987                 n = LittleLong(*in);
3988                 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3989                         Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3990                 *out = loadmodel->brushq3.data_brushes + n;
3991         }
3992 }
3993
3994 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3995 {
3996         int *in;
3997         q3mface_t **out;
3998         int i, n, count;
3999
4000         in = (void *)(mod_base + l->fileofs);
4001         if (l->filelen % sizeof(*in))
4002                 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4003         count = l->filelen / sizeof(*in);
4004         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4005
4006         loadmodel->brushq3.data_leaffaces = out;
4007         loadmodel->brushq3.num_leaffaces = count;
4008
4009         for (i = 0;i < count;i++, in++, out++)
4010         {
4011                 n = LittleLong(*in);
4012                 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4013                         Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4014                 *out = loadmodel->brushq3.data_faces + n;
4015         }
4016 }
4017
4018 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4019 {
4020         q3dleaf_t *in;
4021         q3mleaf_t *out;
4022         int i, j, n, c, count;
4023
4024         in = (void *)(mod_base + l->fileofs);
4025         if (l->filelen % sizeof(*in))
4026                 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4027         count = l->filelen / sizeof(*in);
4028         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4029
4030         loadmodel->brushq3.data_leafs = out;
4031         loadmodel->brushq3.num_leafs = count;
4032
4033         for (i = 0;i < count;i++, in++, out++)
4034         {
4035                 out->isnode = false;
4036                 out->parent = NULL;
4037                 out->clusterindex = LittleLong(in->clusterindex);
4038                 out->areaindex = LittleLong(in->areaindex);
4039                 for (j = 0;j < 3;j++)
4040                 {
4041                         // yes the mins/maxs are ints
4042                         out->mins[j] = LittleLong(in->mins[j]);
4043                         out->maxs[j] = LittleLong(in->maxs[j]);
4044                 }
4045                 n = LittleLong(in->firstleafface);
4046                 c = LittleLong(in->numleaffaces);
4047                 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4048                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4049                 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4050                 out->numleaffaces = c;
4051                 n = LittleLong(in->firstleafbrush);
4052                 c = LittleLong(in->numleafbrushes);
4053                 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4054                         Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4055                 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4056                 out->numleafbrushes = c;
4057         }
4058 }
4059
4060 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4061 {
4062         if (node->parent)
4063                 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4064         node->parent = parent;
4065         if (node->isnode)
4066         {
4067                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4068                 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4069         }
4070 }
4071
4072 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4073 {
4074         q3dnode_t *in;
4075         q3mnode_t *out;
4076         int i, j, n, count;
4077
4078         in = (void *)(mod_base + l->fileofs);
4079         if (l->filelen % sizeof(*in))
4080                 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4081         count = l->filelen / sizeof(*in);
4082         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4083
4084         loadmodel->brushq3.data_nodes = out;
4085         loadmodel->brushq3.num_nodes = count;
4086
4087         for (i = 0;i < count;i++, in++, out++)
4088         {
4089                 out->isnode = true;
4090                 out->parent = NULL;
4091                 n = LittleLong(in->planeindex);
4092                 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4093                         Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4094                 out->plane = loadmodel->brushq3.data_planes + n;
4095                 for (j = 0;j < 2;j++)
4096                 {
4097                         n = LittleLong(in->childrenindex[j]);
4098                         if (n >= 0)
4099                         {
4100                                 if (n >= loadmodel->brushq3.num_nodes)
4101                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4102                                 out->children[j] = loadmodel->brushq3.data_nodes + n;
4103                         }
4104                         else
4105                         {
4106                                 n = 1 - n;
4107                                 if (n >= loadmodel->brushq3.num_leafs)
4108                                         Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4109                                 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4110                         }
4111                 }
4112                 // we don't load the mins/maxs
4113         }
4114
4115         // set the parent pointers
4116         Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4117 }
4118
4119 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4120 {
4121         q3dlightgrid_t *in;
4122         q3dlightgrid_t *out;
4123         int count;
4124
4125         in = (void *)(mod_base + l->fileofs);
4126         if (l->filelen % sizeof(*in))
4127                 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4128         loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4129         loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4130         loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4131         loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4132         loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4133         loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4134         loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4135         loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4136         loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4137         loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4138         loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4139         loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4140         count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4141         if (l->filelen < count * (int)sizeof(*in))
4142                 Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)\n", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
4143         if (l->filelen != count * (int)sizeof(*in))
4144                 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4145
4146         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4147         loadmodel->brushq3.data_lightgrid = out;
4148         loadmodel->brushq3.num_lightgrid = count;
4149
4150         // no swapping or validation necessary
4151         memcpy(out, in, count * (int)sizeof(*out));
4152
4153         Matrix4x4_CreateScale3(&loadmodel->brushq3.num_lightgrid_indexfromworld, loadmodel->brushq3.num_lightgrid_scale[0], loadmodel->brushq3.num_lightgrid_scale[1], loadmodel->brushq3.num_lightgrid_scale[2]);
4154         Matrix4x4_ConcatTranslate(&loadmodel->brushq3.num_lightgrid_indexfromworld, -loadmodel->brushq3.num_lightgrid_imins[0] * loadmodel->brushq3.num_lightgrid_cellsize[0], -loadmodel->brushq3.num_lightgrid_imins[1] * loadmodel->brushq3.num_lightgrid_cellsize[1], -loadmodel->brushq3.num_lightgrid_imins[2] * loadmodel->brushq3.num_lightgrid_cellsize[2]);
4155 }
4156
4157 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4158 {
4159         q3dpvs_t *in;
4160         int totalchains;
4161
4162         in = (void *)(mod_base + l->fileofs);
4163         if (l->filelen < 9)
4164                 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4165
4166         loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4167         loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4168         if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4169                 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4170         totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4171         if (l->filelen < totalchains + (int)sizeof(*in))
4172                 Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)\n", loadmodel->brushq3.num_pvsclusters, loadmodel->brushq3.num_pvschainlength, totalchains + sizeof(*in), l->filelen);
4173
4174         loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4175         memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4176 }
4177
4178 void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4179 {
4180         // FIXME: finish this code
4181         VectorCopy(in, out);
4182 }
4183
4184 void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end)
4185 {
4186         if (node->isnode)
4187         {
4188                 // recurse down node sides
4189                 int i;
4190                 float dist;
4191                 colpointf_t *ps, *pe;
4192                 // FIXME? if TraceBrushPolygonTransform were to be made usable, the
4193                 // node planes would need to be transformed too
4194                 dist = node->plane->dist - (1.0f / 8.0f);
4195                 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4196                 {
4197                         if (DotProduct(ps->v, node->plane->normal) > dist || DotProduct(pe->v, node->plane->normal) > dist)
4198                         {
4199                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end);
4200                                 break;
4201                         }
4202                 }
4203                 dist = node->plane->dist + (1.0f / 8.0f);
4204                 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4205                 {
4206                         if (DotProduct(ps->v, node->plane->normal) < dist || DotProduct(pe->v, node->plane->normal) < dist)
4207                         {
4208                                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end);
4209                                 break;
4210                         }
4211                 }
4212                 /*
4213                 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4214                 if (sides & 1)
4215                         Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4216                 if (sides & 2)
4217                         Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4218                 */
4219         }
4220         else
4221         {
4222                 int i;
4223                 q3mleaf_t *leaf;
4224                 leaf = (q3mleaf_t *)node;
4225                 for (i = 0;i < leaf->numleafbrushes;i++)
4226                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4227         }
4228 }
4229
4230 void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs)
4231 {
4232         int i;
4233         colbrushf_t *thisbrush_start, *thisbrush_end;
4234         matrix4x4_t startmatrix, endmatrix;
4235         // FIXME: finish this code
4236         Matrix4x4_CreateIdentity(&startmatrix);
4237         Matrix4x4_CreateIdentity(&endmatrix);
4238         thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4239         thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4240         memset(trace, 0, sizeof(*trace));
4241         trace->fraction = 1;
4242         if (model->brushq3.num_nodes)
4243                 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end);
4244         else
4245                 for (i = 0;i < model->brushq3.num_brushes;i++)
4246                         Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_brushes[i].colbrushf, model->brushq3.data_brushes[i].colbrushf);
4247 }
4248
4249 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4250 {
4251         int i;
4252         q3dheader_t *header;
4253
4254         mod->type = mod_brushq2;
4255
4256         header = (q3dheader_t *)buffer;
4257
4258         i = LittleLong(header->version);
4259         if (i != Q3BSPVERSION)
4260                 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4261         if (loadmodel->isworldmodel)
4262         {
4263                 Cvar_SetValue("halflifebsp", false);
4264                 // until we get a texture for it...
4265                 R_ResetQuakeSky();
4266         }
4267
4268         mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4269         mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4270         //mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
4271         //mod->brushq1.LeafPVS = Mod_Q1BSP_LeafPVS;
4272         //mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
4273
4274         mod_base = (qbyte *)header;
4275
4276         // swap all the lumps
4277         for (i = 0;i < (int) sizeof(*header) / 4;i++)
4278                 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4279
4280         Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4281         Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4282         Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4283         Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4284         Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4285         Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4286         Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4287         Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4288         Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4289         Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4290         Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4291         Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4292         Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4293         Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4294         Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4295         Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4296         Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4297 }
4298
4299 void Mod_IBSP_Load(model_t *mod, void *buffer)
4300 {
4301         int i = LittleLong(((int *)buffer)[1]);
4302         if (i == Q3BSPVERSION)
4303                 Mod_Q3BSP_Load(mod,buffer);
4304         else if (i == Q2BSPVERSION)
4305                 Mod_Q2BSP_Load(mod,buffer);
4306         else
4307                 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4308 }
4309
4310 void Mod_MAP_Load(model_t *mod, void *buffer)
4311 {
4312         Host_Error("Mod_MAP_Load: not yet implemented\n");
4313 }
4314