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