2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
27 // note: model_shared.c sets up r_notexture, and r_surf_notexture
29 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
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", "1"};
41 void Mod_BrushInit(void)
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));
55 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
62 Mod_CheckLoaded(model);
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];
70 return (mleaf_t *)node;
73 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
77 leaf = Mod_Q1BSP_PointInLeaf(model, p);
80 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
83 memcpy(out, leaf->ambient_sound_level, i);
89 memset(out, 0, outsize);
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)
97 if (node->contents < 0)
100 if (node->contents == CONTENTS_SOLID)
102 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
103 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
106 // node - recurse down the BSP tree
107 switch (BoxOnPlaneSide(mins, maxs, node->plane))
110 node = node->children[0];
113 node = node->children[1];
116 if (node->children[0]->contents != CONTENTS_SOLID)
117 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
119 node = node->children[1];
126 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
128 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
132 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
137 return CONTENTS_EMPTY;
139 Mod_CheckLoaded(model);
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];
147 return ((mleaf_t *)node)->contents;
151 typedef struct findnonsolidlocationinfo_s
159 findnonsolidlocationinfo_t;
162 extern cvar_t samelevel;
164 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
166 int i, surfnum, k, *tri, *mark;
167 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
172 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
174 surf = info->model->brushq1.surfaces + *mark;
175 if (surf->flags & SURF_SOLIDCLIP)
178 VectorCopy(surf->plane->normal, surfnormal);
179 if (surf->flags & SURF_PLANEBACK)
180 VectorNegate(surfnormal, surfnormal);
182 for (k = 0;k < surf->mesh.num_triangles;k++)
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])
193 VectorNormalize(facenormal);
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]);
198 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
199 if (f <= info->bestdist && f >= -info->bestdist)
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]);
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]);
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]))
226 // we got lucky, the center is within the face
227 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
231 if (info->bestdist > dist)
233 info->bestdist = dist;
234 VectorScale(facenormal, (info->radius - -dist), info->nudge);
239 if (info->bestdist > dist)
241 info->bestdist = dist;
242 VectorScale(facenormal, (info->radius - dist), info->nudge);
248 // check which edge or vertex the center is nearest
249 for (i = 0;i < 3;i++)
251 f = DotProduct(info->center, edge[i]);
252 if (f >= DotProduct(vert[0], edge[i])
253 && f <= DotProduct(vert[1], edge[i]))
256 VectorMA(info->center, -f, edge[i], point);
257 dist = sqrt(DotProduct(point, point));
258 if (info->bestdist > dist)
260 info->bestdist = dist;
261 VectorScale(point, (info->radius / dist), info->nudge);
263 // skip both vertex checks
264 // (both are further away than this edge)
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)
274 info->bestdist = dist;
275 VectorScale(point, (info->radius / dist), info->nudge);
287 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
291 if (((mleaf_t *)node)->nummarksurfaces)
292 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
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]);
304 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
307 findnonsolidlocationinfo_t info;
313 VectorCopy(in, info.center);
314 info.radius = radius;
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);
324 while (info.bestdist < radius && ++i < 10);
325 VectorCopy(info.center, out);
328 int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
330 switch(nativecontents)
335 return SUPERCONTENTS_SOLID;
337 return SUPERCONTENTS_WATER;
339 return SUPERCONTENTS_SLIME;
341 return SUPERCONTENTS_LAVA;
343 return SUPERCONTENTS_SKY;
348 int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
350 if (supercontents & SUPERCONTENTS_SOLID)
351 return CONTENTS_SOLID;
352 if (supercontents & SUPERCONTENTS_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;
365 // the hull we're tracing through
368 // the trace structure to fill in
371 // start, end, and end - start (in model space)
376 RecursiveHullCheckTraceInfo_t;
378 // 1/32 epsilon to keep floating point happy
379 #define DIST_EPSILON (0.03125)
381 #define HULLCHECKSTATE_EMPTY 0
382 #define HULLCHECKSTATE_SOLID 1
383 #define HULLCHECKSTATE_DONE 2
385 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
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)
394 // variables that need to be stored on the stack when recursing
399 // LordHavoc: a goto! everyone flee in terror... :)
404 num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num);
405 if (!t->trace->startfound)
407 t->trace->startfound = true;
408 t->trace->startsupercontents |= num;
410 if (num & SUPERCONTENTS_LIQUIDSMASK)
411 t->trace->inwater = true;
413 t->trace->inopen = true;
414 if (num & t->trace->hitsupercontentsmask)
416 // if the first leaf is solid, set startsolid
417 if (t->trace->allsolid)
418 t->trace->startsolid = true;
419 return HULLCHECKSTATE_SOLID;
423 t->trace->allsolid = false;
424 return HULLCHECKSTATE_EMPTY;
428 // find the point distances
429 node = t->hull->clipnodes + num;
431 plane = t->hull->planes + node->planenum;
434 t1 = p1[plane->type] - plane->dist;
435 t2 = p2[plane->type] - plane->dist;
439 t1 = DotProduct (plane->normal, p1) - plane->dist;
440 t2 = DotProduct (plane->normal, p2) - plane->dist;
447 num = node->children[1];
456 num = node->children[0];
462 // the line intersects, find intersection point
463 // LordHavoc: this uses the original trace for maximum accuracy
466 t1 = t->start[plane->type] - plane->dist;
467 t2 = t->end[plane->type] - plane->dist;
471 t1 = DotProduct (plane->normal, t->start) - plane->dist;
472 t2 = DotProduct (plane->normal, t->end) - plane->dist;
475 midf = t1 / (t1 - t2);
476 midf = bound(p1f, midf, p2f);
477 VectorMA(t->start, midf, t->dist, mid);
479 // recurse both sides, front side first
480 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
481 // if this side is not empty, return what it is (solid or done)
482 if (ret != HULLCHECKSTATE_EMPTY)
485 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
486 // if other side is not solid, return what it is (empty or done)
487 if (ret != HULLCHECKSTATE_SOLID)
490 // front is air and back is solid, this is the impact point...
493 t->trace->plane.dist = -plane->dist;
494 VectorNegate (plane->normal, t->trace->plane.normal);
498 t->trace->plane.dist = plane->dist;
499 VectorCopy (plane->normal, t->trace->plane.normal);
502 // bias away from surface a bit
503 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
504 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
506 midf = t1 / (t1 - t2);
507 t->trace->fraction = bound(0.0f, midf, 1.0);
509 return HULLCHECKSTATE_DONE;
512 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)
514 // this function currently only supports same size start and end
516 RecursiveHullCheckTraceInfo_t rhc;
518 memset(&rhc, 0, sizeof(rhc));
519 memset(trace, 0, sizeof(trace_t));
521 rhc.trace->hitsupercontentsmask = hitsupercontentsmask;
522 rhc.trace->fraction = 1;
523 rhc.trace->allsolid = true;
524 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
526 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
527 else if (model->brush.ishlbsp)
529 if (boxsize[0] <= 32)
531 if (boxsize[2] < 54) // pick the nearest of 36 or 72
532 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
534 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
537 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
541 if (boxsize[0] <= 32)
542 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
544 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
546 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
547 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
548 VectorSubtract(rhc.end, rhc.start, rhc.dist);
549 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
552 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)
554 int side, distz = endz - startz;
559 if (node->contents < 0)
560 return false; // didn't hit anything
562 switch (node->plane->type)
565 node = node->children[x < node->plane->dist];
568 node = node->children[y < node->plane->dist];
571 side = startz < node->plane->dist;
572 if ((endz < node->plane->dist) == side)
574 node = node->children[side];
577 // found an intersection
578 mid = node->plane->dist;
581 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
582 front += startz * node->plane->normal[2];
583 back += endz * node->plane->normal[2];
584 side = front < node->plane->dist;
585 if ((back < node->plane->dist) == side)
587 node = node->children[side];
590 // found an intersection
591 mid = startz + distz * (front - node->plane->dist) / (front - back);
595 // go down front side
596 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
597 return true; // hit something
600 // check for impact on this node
601 if (node->numsurfaces)
606 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
607 for (i = 0;i < node->numsurfaces;i++, surf++)
609 if (!(surf->flags & SURF_LIGHTMAP))
610 continue; // no lightmaps
612 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]);
613 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]);
615 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
618 ds -= surf->texturemins[0];
619 dt -= surf->texturemins[1];
621 if (ds > surf->extents[0] || dt > surf->extents[1])
627 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;
628 line3 = ((surf->extents[0]>>4)+1)*3;
629 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
631 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
633 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
635 scale = d_lightstylevalue[surf->styles[maps]];
636 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
637 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
638 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
639 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
644 LordHavoc: here's the readable version of the interpolation
645 code, not quite as easy for the compiler to optimize...
647 dsfrac is the X position in the lightmap pixel, * 16
648 dtfrac is the Y position in the lightmap pixel, * 16
649 r00 is top left corner, r01 is top right corner
650 r10 is bottom left corner, r11 is bottom right corner
651 g and b are the same layout.
652 r0 and r1 are the top and bottom intermediate results
654 first we interpolate the top two points, to get the top
657 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
658 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
659 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
661 then we interpolate the bottom two points, to get the
664 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
665 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
666 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
668 then we interpolate the top and bottom samples to get the
669 middle sample (the one which was requested)
671 r = (((r1-r0) * dtfrac) >> 4) + r0;
672 g = (((g1-g0) * dtfrac) >> 4) + g0;
673 b = (((b1-b0) * dtfrac) >> 4) + b0;
676 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
677 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
678 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
680 return true; // success
685 node = node->children[side ^ 1];
687 distz = endz - startz;
692 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
694 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);
697 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
704 Con_DPrintf("Mod_Q1BSP_DecompressVis: input underrun on model \"%s\"\n", loadmodel->name);
712 for (c = *in++;c > 0;c--)
716 Con_DPrintf("Mod_Q1BSP_DecompressVis: output overrun on model \"%s\"\n", loadmodel->name);
725 static void Mod_Q1BSP_LoadTextures(lump_t *l)
727 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
729 texture_t *tx, *tx2, *anims[10], *altanims[10];
731 qbyte *data, *mtdata;
734 loadmodel->brushq1.textures = NULL;
736 // add two slots for notexture walls and notexture liquids
739 m = (dmiptexlump_t *)(mod_base + l->fileofs);
740 m->nummiptex = LittleLong (m->nummiptex);
741 loadmodel->brushq1.numtextures = m->nummiptex + 2;
746 loadmodel->brushq1.numtextures = 2;
749 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
751 // fill out all slots with notexture
752 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
755 strcpy(tx->name, "NO TEXTURE FOUND");
758 tx->skin.base = r_notexture;
759 tx->shader = &Cshader_wall_lightmap;
760 tx->flags = SURF_SOLIDCLIP;
761 if (i == loadmodel->brushq1.numtextures - 1)
763 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
764 tx->shader = &Cshader_water;
766 tx->currentframe = tx;
772 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
774 // LordHavoc: mostly rewritten map texture loader
775 for (i = 0;i < m->nummiptex;i++)
777 dofs[i] = LittleLong(dofs[i]);
778 if (dofs[i] == -1 || r_nosurftextures.integer)
780 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
782 // make sure name is no more than 15 characters
783 for (j = 0;dmiptex->name[j] && j < 15;j++)
784 name[j] = dmiptex->name[j];
787 mtwidth = LittleLong(dmiptex->width);
788 mtheight = LittleLong(dmiptex->height);
790 j = LittleLong(dmiptex->offsets[0]);
794 if (j < 40 || j + mtwidth * mtheight > l->filelen)
796 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
799 mtdata = (qbyte *)dmiptex + j;
802 if ((mtwidth & 15) || (mtheight & 15))
803 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
805 // LordHavoc: force all names to lowercase
806 for (j = 0;name[j];j++)
807 if (name[j] >= 'A' && name[j] <= 'Z')
808 name[j] += 'a' - 'A';
810 tx = loadmodel->brushq1.textures + i;
811 strcpy(tx->name, name);
813 tx->height = mtheight;
817 sprintf(tx->name, "unnamed%i", i);
818 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
821 // LordHavoc: HL sky textures are entirely different than quake
822 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
824 if (loadmodel->isworldmodel)
826 data = loadimagepixels(tx->name, false, 0, 0);
829 if (image_width == 256 && image_height == 128)
837 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
839 R_InitSky(mtdata, 1);
842 else if (mtdata != NULL)
843 R_InitSky(mtdata, 1);
848 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
850 // did not find external texture, load it from the bsp or wad3
851 if (loadmodel->brush.ishlbsp)
853 // internal texture overrides wad
854 qbyte *pixels, *freepixels, *fogpixels;
855 pixels = freepixels = NULL;
857 pixels = W_ConvertWAD3Texture(dmiptex);
859 pixels = freepixels = W_GetTexture(tx->name);
862 tx->width = image_width;
863 tx->height = image_height;
864 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);
865 if (Image_CheckAlpha(pixels, image_width * image_height, true))
867 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
868 for (j = 0;j < image_width * image_height * 4;j += 4)
870 fogpixels[j + 0] = 255;
871 fogpixels[j + 1] = 255;
872 fogpixels[j + 2] = 255;
873 fogpixels[j + 3] = pixels[j + 3];
875 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
880 Mem_Free(freepixels);
882 else if (mtdata) // texture included
883 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
886 if (tx->skin.base == NULL)
891 tx->skin.base = r_notexture;
894 if (tx->name[0] == '*')
896 // turb does not block movement
897 tx->flags &= ~SURF_SOLIDCLIP;
898 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
899 // LordHavoc: some turbulent textures should be fullbright and solid
900 if (!strncmp(tx->name,"*lava",5)
901 || !strncmp(tx->name,"*teleport",9)
902 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
903 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
905 tx->flags |= SURF_WATERALPHA;
906 tx->shader = &Cshader_water;
908 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
910 tx->flags |= SURF_DRAWSKY;
911 tx->shader = &Cshader_sky;
915 tx->flags |= SURF_LIGHTMAP;
917 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
918 tx->shader = &Cshader_wall_lightmap;
921 // start out with no animation
922 tx->currentframe = tx;
925 // sequence the animations
926 for (i = 0;i < m->nummiptex;i++)
928 tx = loadmodel->brushq1.textures + i;
929 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
931 if (tx->anim_total[0] || tx->anim_total[1])
932 continue; // already sequenced
934 // find the number of frames in the animation
935 memset(anims, 0, sizeof(anims));
936 memset(altanims, 0, sizeof(altanims));
938 for (j = i;j < m->nummiptex;j++)
940 tx2 = loadmodel->brushq1.textures + j;
941 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
945 if (num >= '0' && num <= '9')
946 anims[num - '0'] = tx2;
947 else if (num >= 'a' && num <= 'j')
948 altanims[num - 'a'] = tx2;
950 Con_Printf("Bad animating texture %s\n", tx->name);
954 for (j = 0;j < 10;j++)
961 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
964 for (j = 0;j < max;j++)
968 Con_Printf("Missing frame %i of %s\n", j, tx->name);
972 for (j = 0;j < altmax;j++)
976 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
985 // if there is no alternate animation, duplicate the primary
986 // animation into the alternate
988 for (k = 0;k < 10;k++)
989 altanims[k] = anims[k];
992 // link together the primary animation
993 for (j = 0;j < max;j++)
996 tx2->animated = true;
997 tx2->anim_total[0] = max;
998 tx2->anim_total[1] = altmax;
999 for (k = 0;k < 10;k++)
1001 tx2->anim_frames[0][k] = anims[k];
1002 tx2->anim_frames[1][k] = altanims[k];
1006 // if there really is an alternate anim...
1007 if (anims[0] != altanims[0])
1009 // link together the alternate animation
1010 for (j = 0;j < altmax;j++)
1013 tx2->animated = true;
1014 // the primary/alternate are reversed here
1015 tx2->anim_total[0] = altmax;
1016 tx2->anim_total[1] = max;
1017 for (k = 0;k < 10;k++)
1019 tx2->anim_frames[0][k] = altanims[k];
1020 tx2->anim_frames[1][k] = anims[k];
1027 static void Mod_Q1BSP_LoadLighting(lump_t *l)
1030 qbyte *in, *out, *data, d;
1031 char litfilename[1024];
1032 loadmodel->brushq1.lightdata = NULL;
1033 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1035 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1036 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1038 else // LordHavoc: bsp version 29 (normal white lighting)
1040 // LordHavoc: hope is not lost yet, check for a .lit file to load
1041 strlcpy (litfilename, loadmodel->name, sizeof (litfilename));
1042 FS_StripExtension (litfilename, litfilename, sizeof (litfilename));
1043 strlcat (litfilename, ".lit", sizeof (litfilename));
1044 data = (qbyte*) FS_LoadFile(litfilename, false);
1047 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1049 i = LittleLong(((int *)data)[1]);
1052 Con_DPrintf("loaded %s\n", litfilename);
1053 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1054 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1060 Con_Printf("Unknown .lit file version (%d)\n", i);
1066 if (fs_filesize == 8)
1067 Con_Printf("Empty .lit file, ignoring\n");
1069 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1073 // LordHavoc: oh well, expand the white lighting data
1076 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1077 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1078 out = loadmodel->brushq1.lightdata;
1079 memcpy(in, mod_base + l->fileofs, l->filelen);
1080 for (i = 0;i < l->filelen;i++)
1090 static void Mod_Q1BSP_LoadLightList(void)
1092 int a, n, numlights;
1093 char lightsfilename[1024], *s, *t, *lightsstring;
1096 strlcpy (lightsfilename, loadmodel->name, sizeof (lightsfilename));
1097 FS_StripExtension (lightsfilename, lightsfilename, sizeof(lightsfilename));
1098 strlcat (lightsfilename, ".lights", sizeof (lightsfilename));
1099 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1105 while (*s && *s != '\n')
1109 Mem_Free(lightsstring);
1110 Host_Error("lights file must end with a newline\n");
1115 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1118 while (*s && n < numlights)
1121 while (*s && *s != '\n')
1125 Mem_Free(lightsstring);
1126 Host_Error("misparsed lights file!\n");
1128 e = loadmodel->brushq1.lights + n;
1130 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);
1134 Mem_Free(lightsstring);
1135 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);
1142 Mem_Free(lightsstring);
1143 Host_Error("misparsed lights file!\n");
1145 loadmodel->brushq1.numlights = numlights;
1146 Mem_Free(lightsstring);
1150 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1152 loadmodel->brushq1.num_compressedpvs = 0;
1153 loadmodel->brushq1.data_compressedpvs = NULL;
1156 loadmodel->brushq1.num_compressedpvs = l->filelen;
1157 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1158 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1161 // used only for HalfLife maps
1162 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1164 char key[128], value[4096];
1169 if (!COM_ParseToken(&data, false))
1171 if (com_token[0] != '{')
1175 if (!COM_ParseToken(&data, false))
1177 if (com_token[0] == '}')
1178 break; // end of worldspawn
1179 if (com_token[0] == '_')
1180 strcpy(key, com_token + 1);
1182 strcpy(key, com_token);
1183 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1184 key[strlen(key)-1] = 0;
1185 if (!COM_ParseToken(&data, false))
1187 strcpy(value, com_token);
1188 if (!strcmp("wad", key)) // for HalfLife maps
1190 if (loadmodel->brush.ishlbsp)
1193 for (i = 0;i < 4096;i++)
1194 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1200 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1201 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1203 else if (value[i] == ';' || value[i] == 0)
1207 strcpy(wadname, "textures/");
1208 strcat(wadname, &value[j]);
1209 W_LoadTextureWadFile(wadname, false);
1221 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1223 loadmodel->brush.entities = NULL;
1226 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1227 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1228 if (loadmodel->brush.ishlbsp)
1229 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1233 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1239 in = (void *)(mod_base + l->fileofs);
1240 if (l->filelen % sizeof(*in))
1241 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1242 count = l->filelen / sizeof(*in);
1243 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1245 loadmodel->brushq1.vertexes = out;
1246 loadmodel->brushq1.numvertexes = count;
1248 for ( i=0 ; i<count ; i++, in++, out++)
1250 out->position[0] = LittleFloat(in->point[0]);
1251 out->position[1] = LittleFloat(in->point[1]);
1252 out->position[2] = LittleFloat(in->point[2]);
1256 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1262 in = (void *)(mod_base + l->fileofs);
1263 if (l->filelen % sizeof(*in))
1264 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1265 count = l->filelen / sizeof(*in);
1266 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1268 loadmodel->brushq1.submodels = out;
1269 loadmodel->brush.numsubmodels = count;
1271 for ( i=0 ; i<count ; i++, in++, out++)
1273 for (j=0 ; j<3 ; j++)
1275 // spread the mins / maxs by a pixel
1276 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1277 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1278 out->origin[j] = LittleFloat(in->origin[j]);
1280 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1281 out->headnode[j] = LittleLong(in->headnode[j]);
1282 out->visleafs = LittleLong(in->visleafs);
1283 out->firstface = LittleLong(in->firstface);
1284 out->numfaces = LittleLong(in->numfaces);
1288 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1294 in = (void *)(mod_base + l->fileofs);
1295 if (l->filelen % sizeof(*in))
1296 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1297 count = l->filelen / sizeof(*in);
1298 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1300 loadmodel->brushq1.edges = out;
1301 loadmodel->brushq1.numedges = count;
1303 for ( i=0 ; i<count ; i++, in++, out++)
1305 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1306 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1310 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1314 int i, j, k, count, miptex;
1316 in = (void *)(mod_base + l->fileofs);
1317 if (l->filelen % sizeof(*in))
1318 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1319 count = l->filelen / sizeof(*in);
1320 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1322 loadmodel->brushq1.texinfo = out;
1323 loadmodel->brushq1.numtexinfo = count;
1325 for (i = 0;i < count;i++, in++, out++)
1327 for (k = 0;k < 2;k++)
1328 for (j = 0;j < 4;j++)
1329 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1331 miptex = LittleLong(in->miptex);
1332 out->flags = LittleLong(in->flags);
1334 out->texture = NULL;
1335 if (loadmodel->brushq1.textures)
1337 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1338 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1340 out->texture = loadmodel->brushq1.textures + miptex;
1342 if (out->flags & TEX_SPECIAL)
1344 // if texture chosen is NULL or the shader needs a lightmap,
1345 // force to notexture water shader
1346 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1347 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1351 // if texture chosen is NULL, force to notexture
1352 if (out->texture == NULL)
1353 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1359 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1364 mins[0] = mins[1] = mins[2] = 9999;
1365 maxs[0] = maxs[1] = maxs[2] = -9999;
1367 for (i = 0;i < numverts;i++)
1369 for (j = 0;j < 3;j++, v++)
1379 #define MAX_SUBDIVPOLYTRIANGLES 4096
1380 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1382 static int subdivpolyverts, subdivpolytriangles;
1383 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1384 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1386 static int subdivpolylookupvert(vec3_t v)
1389 for (i = 0;i < subdivpolyverts;i++)
1390 if (subdivpolyvert[i][0] == v[0]
1391 && subdivpolyvert[i][1] == v[1]
1392 && subdivpolyvert[i][2] == v[2])
1394 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1395 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1396 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1397 return subdivpolyverts++;
1400 static void SubdividePolygon(int numverts, float *verts)
1402 int i, i1, i2, i3, f, b, c, p;
1403 vec3_t mins, maxs, front[256], back[256];
1404 float m, *pv, *cv, dist[256], frac;
1407 Host_Error("SubdividePolygon: ran out of verts in buffer");
1409 BoundPoly(numverts, verts, mins, maxs);
1411 for (i = 0;i < 3;i++)
1413 m = (mins[i] + maxs[i]) * 0.5;
1414 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1415 if (maxs[i] - m < 8)
1417 if (m - mins[i] < 8)
1421 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1422 dist[c] = cv[i] - m;
1425 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1429 VectorCopy(pv, front[f]);
1434 VectorCopy(pv, back[b]);
1437 if (dist[p] == 0 || dist[c] == 0)
1439 if ((dist[p] > 0) != (dist[c] > 0) )
1442 frac = dist[p] / (dist[p] - dist[c]);
1443 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1444 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1445 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1451 SubdividePolygon(f, front[0]);
1452 SubdividePolygon(b, back[0]);
1456 i1 = subdivpolylookupvert(verts);
1457 i2 = subdivpolylookupvert(verts + 3);
1458 for (i = 2;i < numverts;i++)
1460 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1462 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1466 i3 = subdivpolylookupvert(verts + i * 3);
1467 subdivpolyindex[subdivpolytriangles][0] = i1;
1468 subdivpolyindex[subdivpolytriangles][1] = i2;
1469 subdivpolyindex[subdivpolytriangles][2] = i3;
1471 subdivpolytriangles++;
1475 //Breaks a polygon up along axial 64 unit
1476 //boundaries so that turbulent and sky warps
1477 //can be done reasonably.
1478 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1484 subdivpolytriangles = 0;
1485 subdivpolyverts = 0;
1486 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1487 if (subdivpolytriangles < 1)
1488 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1490 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1491 mesh->num_vertices = subdivpolyverts;
1492 mesh->num_triangles = subdivpolytriangles;
1493 mesh->vertex = (surfvertex_t *)(mesh + 1);
1494 mesh->index = (int *)(mesh->vertex + mesh->num_vertices);
1495 memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t));
1497 for (i = 0;i < mesh->num_triangles;i++)
1498 for (j = 0;j < 3;j++)
1499 mesh->index[i*3+j] = subdivpolyindex[i][j];
1501 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1503 VectorCopy(subdivpolyvert[i], v->v);
1504 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1505 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1510 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1513 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1514 mesh->num_vertices = numverts;
1515 mesh->num_triangles = numtriangles;
1516 mesh->data_vertex3f = (float *)(mesh + 1);
1517 mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3;
1518 mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2;
1519 mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2;
1520 mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2);
1521 mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3;
1522 mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3;
1523 mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3);
1524 mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices;
1525 mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3;
1529 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1532 float *vec, *vert, mins[3], maxs[3], val, *v;
1535 // convert edges back to a normal polygon
1536 surf->poly_numverts = numedges;
1537 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1538 for (i = 0;i < numedges;i++)
1540 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1542 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1544 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1545 VectorCopy(vec, vert);
1549 // calculate polygon bounding box and center
1550 vert = surf->poly_verts;
1551 VectorCopy(vert, mins);
1552 VectorCopy(vert, maxs);
1554 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1556 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1557 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1558 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1560 VectorCopy(mins, surf->poly_mins);
1561 VectorCopy(maxs, surf->poly_maxs);
1562 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1563 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1564 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1566 // generate surface extents information
1567 tex = surf->texinfo;
1568 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1569 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1570 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1572 for (j = 0;j < 2;j++)
1574 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1581 for (i = 0;i < 2;i++)
1583 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1584 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1588 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1592 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1596 in = (void *)(mod_base + l->fileofs);
1597 if (l->filelen % sizeof(*in))
1598 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1599 count = l->filelen / sizeof(*in);
1600 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1602 loadmodel->brushq1.numsurfaces = count;
1603 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1604 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1605 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1607 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++)
1609 surf->number = surfnum;
1610 // FIXME: validate edges, texinfo, etc?
1611 firstedge = LittleLong(in->firstedge);
1612 numedges = LittleShort(in->numedges);
1613 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)
1614 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1615 i = LittleShort(in->texinfo);
1616 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1617 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1618 surf->texinfo = loadmodel->brushq1.texinfo + i;
1619 surf->flags = surf->texinfo->texture->flags;
1621 planenum = LittleShort(in->planenum);
1622 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1623 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1625 if (LittleShort(in->side))
1626 surf->flags |= SURF_PLANEBACK;
1628 surf->plane = loadmodel->brushq1.planes + planenum;
1630 // clear lightmap (filled in later)
1631 surf->lightmaptexture = NULL;
1633 // force lightmap upload on first time seeing the surface
1634 surf->cached_dlight = true;
1636 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1638 ssize = (surf->extents[0] >> 4) + 1;
1639 tsize = (surf->extents[1] >> 4) + 1;
1642 for (i = 0;i < MAXLIGHTMAPS;i++)
1643 surf->styles[i] = in->styles[i];
1644 i = LittleLong(in->lightofs);
1646 surf->samples = NULL;
1647 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1648 surf->samples = loadmodel->brushq1.lightdata + i;
1649 else // LordHavoc: white lighting (bsp version 29)
1650 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1652 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1654 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1655 Host_Error("Bad surface extents");
1656 // stainmap for permanent marks on walls
1657 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1659 memset(surf->stainsamples, 255, ssize * tsize * 3);
1663 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1665 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++)
1668 mesh->num_vertices = surf->poly_numverts;
1669 mesh->num_triangles = surf->poly_numverts - 2;
1670 mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3;
1671 mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2;
1672 mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2;
1673 mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2;
1674 mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3;
1675 mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3;
1676 mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3;
1677 mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts;
1678 mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3;
1679 mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3;
1681 surf->lightmaptexturestride = 0;
1682 surf->lightmaptexture = NULL;
1684 for (i = 0;i < mesh->num_vertices;i++)
1686 mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1687 mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1688 mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1689 s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1690 t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1691 mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1692 mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1693 mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1694 mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1695 mesh->data_texcoordlightmap2f[i * 2 + 0] = 0;
1696 mesh->data_texcoordlightmap2f[i * 2 + 1] = 0;
1697 mesh->data_lightmapoffsets[i] = 0;
1700 for (i = 0;i < mesh->num_triangles;i++)
1702 mesh->data_element3i[i * 3 + 0] = 0;
1703 mesh->data_element3i[i * 3 + 1] = i + 1;
1704 mesh->data_element3i[i * 3 + 2] = i + 2;
1707 Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles);
1708 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);
1710 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1712 int i, iu, iv, smax, tmax;
1713 float u, v, ubase, vbase, uscale, vscale;
1715 smax = surf->extents[0] >> 4;
1716 tmax = surf->extents[1] >> 4;
1718 surf->flags |= SURF_LIGHTMAP;
1719 if (r_miplightmaps.integer)
1721 surf->lightmaptexturestride = smax+1;
1722 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);
1726 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1727 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);
1729 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1730 uscale = (uscale - ubase) / (smax + 1);
1731 vscale = (vscale - vbase) / (tmax + 1);
1733 for (i = 0;i < mesh->num_vertices;i++)
1735 u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1736 v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1737 mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1738 mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1739 // LordHavoc: calc lightmap data offset for vertex lighting to use
1742 mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1748 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1750 node->parent = parent;
1751 if (node->contents < 0)
1753 Mod_Q1BSP_SetParent(node->children[0], node);
1754 Mod_Q1BSP_SetParent(node->children[1], node);
1757 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1763 in = (void *)(mod_base + l->fileofs);
1764 if (l->filelen % sizeof(*in))
1765 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1766 count = l->filelen / sizeof(*in);
1767 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1769 loadmodel->brushq1.nodes = out;
1770 loadmodel->brushq1.numnodes = count;
1772 for ( i=0 ; i<count ; i++, in++, out++)
1774 for (j=0 ; j<3 ; j++)
1776 out->mins[j] = LittleShort(in->mins[j]);
1777 out->maxs[j] = LittleShort(in->maxs[j]);
1780 p = LittleLong(in->planenum);
1781 out->plane = loadmodel->brushq1.planes + p;
1783 out->firstsurface = LittleShort(in->firstface);
1784 out->numsurfaces = LittleShort(in->numfaces);
1786 for (j=0 ; j<2 ; j++)
1788 p = LittleShort(in->children[j]);
1790 out->children[j] = loadmodel->brushq1.nodes + p;
1792 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1796 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1799 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1803 int i, j, count, p, pvschainbytes;
1806 in = (void *)(mod_base + l->fileofs);
1807 if (l->filelen % sizeof(*in))
1808 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1809 count = l->filelen / sizeof(*in);
1810 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1812 loadmodel->brushq1.leafs = out;
1813 loadmodel->brushq1.numleafs = count;
1814 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1815 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1817 for ( i=0 ; i<count ; i++, in++, out++)
1819 for (j=0 ; j<3 ; j++)
1821 out->mins[j] = LittleShort(in->mins[j]);
1822 out->maxs[j] = LittleShort(in->maxs[j]);
1825 // FIXME: this function could really benefit from some error checking
1827 out->contents = LittleLong(in->contents);
1829 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1830 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1831 if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces)
1833 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);
1834 out->firstmarksurface = NULL;
1835 out->nummarksurfaces = 0;
1839 memset(out->pvsdata, 0xFF, pvschainbytes);
1840 pvs += pvschainbytes;
1842 p = LittleLong(in->visofs);
1845 if (p >= loadmodel->brushq1.num_compressedpvs)
1846 Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n");
1848 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1851 for (j = 0;j < 4;j++)
1852 out->ambient_sound_level[j] = in->ambient_level[j];
1854 // FIXME: Insert caustics here
1858 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1860 dclipnode_t *in, *out;
1864 in = (void *)(mod_base + l->fileofs);
1865 if (l->filelen % sizeof(*in))
1866 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1867 count = l->filelen / sizeof(*in);
1868 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1870 loadmodel->brushq1.clipnodes = out;
1871 loadmodel->brushq1.numclipnodes = count;
1873 if (loadmodel->brush.ishlbsp)
1875 hull = &loadmodel->brushq1.hulls[1];
1876 hull->clipnodes = out;
1877 hull->firstclipnode = 0;
1878 hull->lastclipnode = count-1;
1879 hull->planes = loadmodel->brushq1.planes;
1880 hull->clip_mins[0] = -16;
1881 hull->clip_mins[1] = -16;
1882 hull->clip_mins[2] = -36;
1883 hull->clip_maxs[0] = 16;
1884 hull->clip_maxs[1] = 16;
1885 hull->clip_maxs[2] = 36;
1886 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1888 hull = &loadmodel->brushq1.hulls[2];
1889 hull->clipnodes = out;
1890 hull->firstclipnode = 0;
1891 hull->lastclipnode = count-1;
1892 hull->planes = loadmodel->brushq1.planes;
1893 hull->clip_mins[0] = -32;
1894 hull->clip_mins[1] = -32;
1895 hull->clip_mins[2] = -32;
1896 hull->clip_maxs[0] = 32;
1897 hull->clip_maxs[1] = 32;
1898 hull->clip_maxs[2] = 32;
1899 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1901 hull = &loadmodel->brushq1.hulls[3];
1902 hull->clipnodes = out;
1903 hull->firstclipnode = 0;
1904 hull->lastclipnode = count-1;
1905 hull->planes = loadmodel->brushq1.planes;
1906 hull->clip_mins[0] = -16;
1907 hull->clip_mins[1] = -16;
1908 hull->clip_mins[2] = -18;
1909 hull->clip_maxs[0] = 16;
1910 hull->clip_maxs[1] = 16;
1911 hull->clip_maxs[2] = 18;
1912 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1916 hull = &loadmodel->brushq1.hulls[1];
1917 hull->clipnodes = out;
1918 hull->firstclipnode = 0;
1919 hull->lastclipnode = count-1;
1920 hull->planes = loadmodel->brushq1.planes;
1921 hull->clip_mins[0] = -16;
1922 hull->clip_mins[1] = -16;
1923 hull->clip_mins[2] = -24;
1924 hull->clip_maxs[0] = 16;
1925 hull->clip_maxs[1] = 16;
1926 hull->clip_maxs[2] = 32;
1927 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1929 hull = &loadmodel->brushq1.hulls[2];
1930 hull->clipnodes = out;
1931 hull->firstclipnode = 0;
1932 hull->lastclipnode = count-1;
1933 hull->planes = loadmodel->brushq1.planes;
1934 hull->clip_mins[0] = -32;
1935 hull->clip_mins[1] = -32;
1936 hull->clip_mins[2] = -24;
1937 hull->clip_maxs[0] = 32;
1938 hull->clip_maxs[1] = 32;
1939 hull->clip_maxs[2] = 64;
1940 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1943 for (i=0 ; i<count ; i++, out++, in++)
1945 out->planenum = LittleLong(in->planenum);
1946 out->children[0] = LittleShort(in->children[0]);
1947 out->children[1] = LittleShort(in->children[1]);
1948 if (out->children[0] >= count || out->children[1] >= count)
1949 Host_Error("Corrupt clipping hull(out of range child)\n");
1953 //Duplicate the drawing hull structure as a clipping hull
1954 static void Mod_Q1BSP_MakeHull0(void)
1961 hull = &loadmodel->brushq1.hulls[0];
1963 in = loadmodel->brushq1.nodes;
1964 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1966 hull->clipnodes = out;
1967 hull->firstclipnode = 0;
1968 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1969 hull->planes = loadmodel->brushq1.planes;
1971 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1973 out->planenum = in->plane - loadmodel->brushq1.planes;
1974 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1975 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1979 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1984 in = (void *)(mod_base + l->fileofs);
1985 if (l->filelen % sizeof(*in))
1986 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1987 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1988 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1990 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1992 j = (unsigned) LittleShort(in[i]);
1993 if (j >= loadmodel->brushq1.numsurfaces)
1994 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
1995 loadmodel->brushq1.marksurfaces[i] = j;
1999 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2004 in = (void *)(mod_base + l->fileofs);
2005 if (l->filelen % sizeof(*in))
2006 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2007 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2008 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2010 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2011 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2015 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2021 in = (void *)(mod_base + l->fileofs);
2022 if (l->filelen % sizeof(*in))
2023 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2025 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2026 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2028 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2030 out->normal[0] = LittleFloat(in->normal[0]);
2031 out->normal[1] = LittleFloat(in->normal[1]);
2032 out->normal[2] = LittleFloat(in->normal[2]);
2033 out->dist = LittleFloat(in->dist);
2039 typedef struct portal_s
2042 mnode_t *nodes[2]; // [0] = front side of plane
2043 struct portal_s *next[2];
2045 struct portal_s *chain; // all portals are linked into a list
2049 static portal_t *portalchain;
2056 static portal_t *AllocPortal(void)
2059 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2060 p->chain = portalchain;
2065 static void FreePortal(portal_t *p)
2070 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2072 // calculate children first
2073 if (node->children[0]->contents >= 0)
2074 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2075 if (node->children[1]->contents >= 0)
2076 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2078 // make combined bounding box from children
2079 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2080 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2081 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2082 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2083 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2084 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2087 static void Mod_Q1BSP_FinalizePortals(void)
2089 int i, j, numportals, numpoints;
2090 portal_t *p, *pnext;
2093 mleaf_t *leaf, *endleaf;
2096 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2097 leaf = loadmodel->brushq1.leafs;
2098 endleaf = leaf + loadmodel->brushq1.numleafs;
2099 for (;leaf < endleaf;leaf++)
2101 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2102 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2109 for (i = 0;i < 2;i++)
2111 leaf = (mleaf_t *)p->nodes[i];
2113 for (j = 0;j < w->numpoints;j++)
2115 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2116 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2117 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2118 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2119 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2120 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2127 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2129 // tally up portal and point counts
2135 // note: this check must match the one below or it will usually corrupt memory
2136 // 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
2137 if (p->winding && p->nodes[0] != p->nodes[1]
2138 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2139 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2142 numpoints += p->winding->numpoints * 2;
2146 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2147 loadmodel->brushq1.numportals = numportals;
2148 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2149 loadmodel->brushq1.numportalpoints = numpoints;
2150 // clear all leaf portal chains
2151 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2152 loadmodel->brushq1.leafs[i].portals = NULL;
2153 // process all portals in the global portal chain, while freeing them
2154 portal = loadmodel->brushq1.portals;
2155 point = loadmodel->brushq1.portalpoints;
2164 // note: this check must match the one above or it will usually corrupt memory
2165 // 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
2166 if (p->nodes[0] != p->nodes[1]
2167 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2168 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2170 // first make the back to front portal(forward portal)
2171 portal->points = point;
2172 portal->numpoints = p->winding->numpoints;
2173 portal->plane.dist = p->plane.dist;
2174 VectorCopy(p->plane.normal, portal->plane.normal);
2175 portal->here = (mleaf_t *)p->nodes[1];
2176 portal->past = (mleaf_t *)p->nodes[0];
2178 for (j = 0;j < portal->numpoints;j++)
2180 VectorCopy(p->winding->points[j], point->position);
2183 PlaneClassify(&portal->plane);
2185 // link into leaf's portal chain
2186 portal->next = portal->here->portals;
2187 portal->here->portals = portal;
2189 // advance to next portal
2192 // then make the front to back portal(backward portal)
2193 portal->points = point;
2194 portal->numpoints = p->winding->numpoints;
2195 portal->plane.dist = -p->plane.dist;
2196 VectorNegate(p->plane.normal, portal->plane.normal);
2197 portal->here = (mleaf_t *)p->nodes[0];
2198 portal->past = (mleaf_t *)p->nodes[1];
2200 for (j = portal->numpoints - 1;j >= 0;j--)
2202 VectorCopy(p->winding->points[j], point->position);
2205 PlaneClassify(&portal->plane);
2207 // link into leaf's portal chain
2208 portal->next = portal->here->portals;
2209 portal->here->portals = portal;
2211 // advance to next portal
2214 Winding_Free(p->winding);
2226 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2229 Host_Error("AddPortalToNodes: NULL front node");
2231 Host_Error("AddPortalToNodes: NULL back node");
2232 if (p->nodes[0] || p->nodes[1])
2233 Host_Error("AddPortalToNodes: already included");
2234 // 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
2236 p->nodes[0] = front;
2237 p->next[0] = (portal_t *)front->portals;
2238 front->portals = (mportal_t *)p;
2241 p->next[1] = (portal_t *)back->portals;
2242 back->portals = (mportal_t *)p;
2247 RemovePortalFromNode
2250 static void RemovePortalFromNodes(portal_t *portal)
2254 void **portalpointer;
2256 for (i = 0;i < 2;i++)
2258 node = portal->nodes[i];
2260 portalpointer = (void **) &node->portals;
2265 Host_Error("RemovePortalFromNodes: portal not in leaf");
2269 if (portal->nodes[0] == node)
2271 *portalpointer = portal->next[0];
2272 portal->nodes[0] = NULL;
2274 else if (portal->nodes[1] == node)
2276 *portalpointer = portal->next[1];
2277 portal->nodes[1] = NULL;
2280 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2284 if (t->nodes[0] == node)
2285 portalpointer = (void **) &t->next[0];
2286 else if (t->nodes[1] == node)
2287 portalpointer = (void **) &t->next[1];
2289 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2294 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2297 mnode_t *front, *back, *other_node;
2298 mplane_t clipplane, *plane;
2299 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2300 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2302 // if a leaf, we're done
2306 plane = node->plane;
2308 front = node->children[0];
2309 back = node->children[1];
2311 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2313 // create the new portal by generating a polygon for the node plane,
2314 // and clipping it by all of the other portals(which came from nodes above this one)
2315 nodeportal = AllocPortal();
2316 nodeportal->plane = *plane;
2318 nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist);
2319 side = 0; // shut up compiler warning
2320 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2322 clipplane = portal->plane;
2323 if (portal->nodes[0] == portal->nodes[1])
2324 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2325 if (portal->nodes[0] == node)
2327 else if (portal->nodes[1] == node)
2329 clipplane.dist = -clipplane.dist;
2330 VectorNegate(clipplane.normal, clipplane.normal);
2334 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2336 nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true);
2337 if (!nodeportalwinding)
2339 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2344 if (nodeportalwinding)
2346 // if the plane was not clipped on all sides, there was an error
2347 nodeportal->winding = nodeportalwinding;
2348 AddPortalToNodes(nodeportal, front, back);
2351 // split the portals of this node along this node's plane and assign them to the children of this node
2352 // (migrating the portals downward through the tree)
2353 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2355 if (portal->nodes[0] == portal->nodes[1])
2356 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2357 if (portal->nodes[0] == node)
2359 else if (portal->nodes[1] == node)
2362 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2363 nextportal = portal->next[side];
2365 other_node = portal->nodes[!side];
2366 RemovePortalFromNodes(portal);
2368 // cut the portal into two portals, one on each side of the node plane
2369 Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding);
2374 AddPortalToNodes(portal, back, other_node);
2376 AddPortalToNodes(portal, other_node, back);
2382 AddPortalToNodes(portal, front, other_node);
2384 AddPortalToNodes(portal, other_node, front);
2388 // the winding is split
2389 splitportal = AllocPortal();
2390 temp = splitportal->chain;
2391 *splitportal = *portal;
2392 splitportal->chain = temp;
2393 splitportal->winding = backwinding;
2394 Winding_Free(portal->winding);
2395 portal->winding = frontwinding;
2399 AddPortalToNodes(portal, front, other_node);
2400 AddPortalToNodes(splitportal, back, other_node);
2404 AddPortalToNodes(portal, other_node, front);
2405 AddPortalToNodes(splitportal, other_node, back);
2409 Mod_Q1BSP_RecursiveNodePortals(front);
2410 Mod_Q1BSP_RecursiveNodePortals(back);
2413 static void Mod_Q1BSP_MakePortals(void)
2416 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2417 Mod_Q1BSP_FinalizePortals();
2420 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2423 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2424 msurface_t *surf, *s;
2425 float *v0, *v1, *v2, *v3;
2426 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2427 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2428 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2430 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)
2432 if (surf->neighborsurfaces[vertnum])
2434 surf->neighborsurfaces[vertnum] = NULL;
2435 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2437 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2438 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2439 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2442 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2443 if (s->neighborsurfaces[vnum] == surf)
2445 if (vnum < s->poly_numverts)
2447 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)
2449 if (s->neighborsurfaces[vnum] == NULL
2450 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2451 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2453 surf->neighborsurfaces[vertnum] = s;
2454 s->neighborsurfaces[vnum] = surf;
2458 if (vnum < s->poly_numverts)
2466 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2468 int i, j, stylecounts[256], totalcount, remapstyles[256];
2470 memset(stylecounts, 0, sizeof(stylecounts));
2471 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2473 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2474 for (j = 0;j < MAXLIGHTMAPS;j++)
2475 stylecounts[surf->styles[j]]++;
2478 model->brushq1.light_styles = 0;
2479 for (i = 0;i < 255;i++)
2483 remapstyles[i] = model->brushq1.light_styles++;
2484 totalcount += stylecounts[i] + 1;
2489 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2490 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2491 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2492 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2493 model->brushq1.light_styles = 0;
2494 for (i = 0;i < 255;i++)
2496 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2498 for (i = 0;i < model->brushq1.light_styles;i++)
2500 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2501 j += stylecounts[model->brushq1.light_style[i]] + 1;
2503 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2505 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2506 for (j = 0;j < MAXLIGHTMAPS;j++)
2507 if (surf->styles[j] != 255)
2508 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2511 for (i = 0;i < model->brushq1.light_styles;i++)
2513 *model->brushq1.light_styleupdatechains[i] = NULL;
2514 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2515 j += stylecounts[model->brushq1.light_style[i]] + 1;
2519 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2522 for (i = 0;i < model->brushq1.numtextures;i++)
2523 model->brushq1.pvstexturechainslength[i] = 0;
2524 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2526 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2528 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2529 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2532 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2534 if (model->brushq1.pvstexturechainslength[i])
2536 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2537 j += model->brushq1.pvstexturechainslength[i] + 1;
2540 model->brushq1.pvstexturechains[i] = NULL;
2542 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2543 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2544 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2545 for (i = 0;i < model->brushq1.numtextures;i++)
2547 if (model->brushq1.pvstexturechainslength[i])
2549 *model->brushq1.pvstexturechains[i] = NULL;
2550 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2555 static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2560 while (node->contents >= 0)
2562 d = PlaneDiff(org, node->plane);
2564 node = node->children[0];
2565 else if (d < -radius)
2566 node = node->children[1];
2569 // go down both sides
2570 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2571 node = node->children[1];
2575 // if this is a leaf, accumulate the pvs bits
2576 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2577 for (i = 0;i < pvsbytes;i++)
2578 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2581 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2582 //of the given point.
2583 static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2585 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2586 bytes = min(bytes, pvsbufferlength);
2587 memset(pvsbuffer, 0, bytes);
2588 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes);
2592 //Returns PVS data for a given point
2593 //(note: always returns valid data, never NULL)
2594 static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p)
2597 Mod_CheckLoaded(model);
2598 // LordHavoc: modified to start at first clip node,
2599 // in other words: first node of the (sub)model
2600 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
2601 while (node->contents == 0)
2602 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
2603 return ((mleaf_t *)node)->pvsdata;
2606 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2611 VectorSubtract(inmaxs, inmins, size);
2612 if (cmodel->brush.ishlbsp)
2615 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2616 else if (size[0] <= 32)
2618 if (size[2] < 54) // pick the nearest of 36 or 72
2619 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2621 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2624 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2629 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2630 else if (size[0] <= 32)
2631 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2633 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2635 VectorCopy(inmins, outmins);
2636 VectorAdd(inmins, hull->clip_size, outmaxs);
2639 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2640 extern void R_Model_Brush_Draw(entity_render_t *ent);
2641 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2642 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);
2643 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2648 mempool_t *mainmempool;
2650 model_t *originalloadmodel;
2651 float dist, modelyawradius, modelradius, *vec;
2654 mod->type = mod_brush;
2656 header = (dheader_t *)buffer;
2658 i = LittleLong(header->version);
2659 if (i != BSPVERSION && i != 30)
2660 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2661 mod->brush.ishlbsp = i == 30;
2663 mod->soundfromcenter = true;
2664 mod->TraceBox = Mod_Q1BSP_TraceBox;
2665 mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
2666 mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
2667 mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
2668 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2669 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2670 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2671 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2672 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2673 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2674 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2675 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2677 if (loadmodel->isworldmodel)
2679 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2680 // until we get a texture for it...
2684 // swap all the lumps
2685 mod_base = (qbyte *)header;
2687 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2688 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2692 // store which lightmap format to use
2693 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2695 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2696 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2697 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2698 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2699 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2700 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2701 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2702 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2703 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2704 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2705 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2706 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2707 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2708 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2709 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2711 if (mod->brushq1.data_compressedpvs)
2712 Mem_Free(mod->brushq1.data_compressedpvs);
2713 mod->brushq1.data_compressedpvs = NULL;
2714 mod->brushq1.num_compressedpvs = 0;
2716 Mod_Q1BSP_MakeHull0();
2717 Mod_Q1BSP_MakePortals();
2719 if (developer.integer)
2720 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);
2722 mod->numframes = 2; // regular and alternate animation
2724 mainmempool = mod->mempool;
2725 loadname = mod->name;
2727 Mod_Q1BSP_LoadLightList();
2728 originalloadmodel = loadmodel;
2731 // set up the submodels(FIXME: this is confusing)
2733 for (i = 0;i < mod->brush.numsubmodels;i++)
2735 bm = &mod->brushq1.submodels[i];
2737 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2738 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2740 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2741 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2744 mod->brushq1.firstmodelsurface = bm->firstface;
2745 mod->brushq1.nummodelsurfaces = bm->numfaces;
2747 // this gets altered below if sky is used
2748 mod->DrawSky = NULL;
2749 mod->Draw = R_Model_Brush_Draw;
2750 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2751 mod->DrawLight = R_Model_Brush_DrawLight;
2752 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2753 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2754 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2755 Mod_Q1BSP_BuildPVSTextureChains(mod);
2756 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2757 if (mod->brushq1.nummodelsurfaces)
2759 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2760 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2761 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2764 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2766 // we only need to have a drawsky function if it is used(usually only on world model)
2767 if (surf->texinfo->texture->shader == &Cshader_sky)
2768 mod->DrawSky = R_Model_Brush_DrawSky;
2769 // LordHavoc: submodels always clip, even if water
2770 if (mod->brush.numsubmodels - 1)
2771 surf->flags |= SURF_SOLIDCLIP;
2772 // calculate bounding shapes
2773 for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3)
2775 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2776 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2777 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2778 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2779 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2780 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2781 dist = vec[0]*vec[0]+vec[1]*vec[1];
2782 if (modelyawradius < dist)
2783 modelyawradius = dist;
2784 dist += vec[2]*vec[2];
2785 if (modelradius < dist)
2789 modelyawradius = sqrt(modelyawradius);
2790 modelradius = sqrt(modelradius);
2791 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2792 mod->yawmins[2] = mod->normalmins[2];
2793 mod->yawmaxs[2] = mod->normalmaxs[2];
2794 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2795 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2796 mod->radius = modelradius;
2797 mod->radius2 = modelradius * modelradius;
2801 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2802 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2804 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2806 mod->brushq1.visleafs = bm->visleafs;
2808 // LordHavoc: only register submodels if it is the world
2809 // (prevents bsp models from replacing world submodels)
2810 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
2813 // duplicate the basic information
2814 sprintf(name, "*%i", i+1);
2815 loadmodel = Mod_FindName(name);
2817 strcpy(loadmodel->name, name);
2818 // textures and memory belong to the main model
2819 loadmodel->texturepool = NULL;
2820 loadmodel->mempool = NULL;
2825 loadmodel = originalloadmodel;
2826 //Mod_Q1BSP_ProcessLightList();
2829 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2833 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2840 in = (void *)(mod_base + l->fileofs);
2841 if (l->filelen % sizeof(*in))
2842 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2843 count = l->filelen / sizeof(*in);
2844 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2847 loadmodel->num = count;
2849 for (i = 0;i < count;i++, in++, out++)
2855 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2862 in = (void *)(mod_base + l->fileofs);
2863 if (l->filelen % sizeof(*in))
2864 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2865 count = l->filelen / sizeof(*in);
2866 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2869 loadmodel->num = count;
2871 for (i = 0;i < count;i++, in++, out++)
2877 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2884 in = (void *)(mod_base + l->fileofs);
2885 if (l->filelen % sizeof(*in))
2886 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2887 count = l->filelen / sizeof(*in);
2888 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2891 loadmodel->num = count;
2893 for (i = 0;i < count;i++, in++, out++)
2899 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2906 in = (void *)(mod_base + l->fileofs);
2907 if (l->filelen % sizeof(*in))
2908 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2909 count = l->filelen / sizeof(*in);
2910 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2913 loadmodel->num = count;
2915 for (i = 0;i < count;i++, in++, out++)
2921 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2928 in = (void *)(mod_base + l->fileofs);
2929 if (l->filelen % sizeof(*in))
2930 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2931 count = l->filelen / sizeof(*in);
2932 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2935 loadmodel->num = count;
2937 for (i = 0;i < count;i++, in++, out++)
2943 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2950 in = (void *)(mod_base + l->fileofs);
2951 if (l->filelen % sizeof(*in))
2952 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2953 count = l->filelen / sizeof(*in);
2954 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2957 loadmodel->num = count;
2959 for (i = 0;i < count;i++, in++, out++)
2965 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2972 in = (void *)(mod_base + l->fileofs);
2973 if (l->filelen % sizeof(*in))
2974 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2975 count = l->filelen / sizeof(*in);
2976 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2979 loadmodel->num = count;
2981 for (i = 0;i < count;i++, in++, out++)
2987 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
2994 in = (void *)(mod_base + l->fileofs);
2995 if (l->filelen % sizeof(*in))
2996 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
2997 count = l->filelen / sizeof(*in);
2998 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3001 loadmodel->num = count;
3003 for (i = 0;i < count;i++, in++, out++)
3009 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3016 in = (void *)(mod_base + l->fileofs);
3017 if (l->filelen % sizeof(*in))
3018 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3019 count = l->filelen / sizeof(*in);
3020 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3023 loadmodel->num = count;
3025 for (i = 0;i < count;i++, in++, out++)
3031 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3038 in = (void *)(mod_base + l->fileofs);
3039 if (l->filelen % sizeof(*in))
3040 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3041 count = l->filelen / sizeof(*in);
3042 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3045 loadmodel->num = count;
3047 for (i = 0;i < count;i++, in++, out++)
3053 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3060 in = (void *)(mod_base + l->fileofs);
3061 if (l->filelen % sizeof(*in))
3062 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3063 count = l->filelen / sizeof(*in);
3064 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3067 loadmodel->num = count;
3069 for (i = 0;i < count;i++, in++, out++)
3075 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3082 in = (void *)(mod_base + l->fileofs);
3083 if (l->filelen % sizeof(*in))
3084 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3085 count = l->filelen / sizeof(*in);
3086 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3089 loadmodel->num = count;
3091 for (i = 0;i < count;i++, in++, out++)
3097 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3104 in = (void *)(mod_base + l->fileofs);
3105 if (l->filelen % sizeof(*in))
3106 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3107 count = l->filelen / sizeof(*in);
3108 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3111 loadmodel->num = count;
3113 for (i = 0;i < count;i++, in++, out++)
3119 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3126 in = (void *)(mod_base + l->fileofs);
3127 if (l->filelen % sizeof(*in))
3128 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3129 count = l->filelen / sizeof(*in);
3130 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3133 loadmodel->num = count;
3135 for (i = 0;i < count;i++, in++, out++)
3141 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3148 in = (void *)(mod_base + l->fileofs);
3149 if (l->filelen % sizeof(*in))
3150 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3151 count = l->filelen / sizeof(*in);
3152 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3155 loadmodel->num = count;
3157 for (i = 0;i < count;i++, in++, out++)
3163 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3170 in = (void *)(mod_base + l->fileofs);
3171 if (l->filelen % sizeof(*in))
3172 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3173 count = l->filelen / sizeof(*in);
3174 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3177 loadmodel->num = count;
3179 for (i = 0;i < count;i++, in++, out++)
3185 static void Mod_Q2BSP_LoadModels(lump_t *l)
3192 in = (void *)(mod_base + l->fileofs);
3193 if (l->filelen % sizeof(*in))
3194 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3195 count = l->filelen / sizeof(*in);
3196 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3199 loadmodel->num = count;
3201 for (i = 0;i < count;i++, in++, out++)
3207 void static Mod_Q2BSP_Load(model_t *mod, void *buffer)
3210 q2dheader_t *header;
3212 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3214 mod->type = mod_brushq2;
3216 header = (q2dheader_t *)buffer;
3218 i = LittleLong(header->version);
3219 if (i != Q2BSPVERSION)
3220 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3221 mod->brush.ishlbsp = false;
3222 if (loadmodel->isworldmodel)
3224 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3225 // until we get a texture for it...
3229 mod_base = (qbyte *)header;
3231 // swap all the lumps
3232 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3233 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3235 // store which lightmap format to use
3236 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3238 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3239 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3240 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3241 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3242 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3243 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3244 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3245 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3246 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3247 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3248 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3249 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3250 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3251 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3252 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3253 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3254 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3255 // LordHavoc: must go last because this makes the submodels
3256 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3259 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents);
3260 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents);
3262 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3265 char key[128], value[4096];
3267 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3268 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3269 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3272 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3273 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3274 data = loadmodel->brush.entities;
3275 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3276 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3280 if (!COM_ParseToken(&data, false))
3282 if (com_token[0] == '}')
3283 break; // end of worldspawn
3284 if (com_token[0] == '_')
3285 strcpy(key, com_token + 1);
3287 strcpy(key, com_token);
3288 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3289 key[strlen(key)-1] = 0;
3290 if (!COM_ParseToken(&data, false))
3292 strcpy(value, com_token);
3293 if (!strcmp("gridsize", key))
3295 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3296 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3302 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3308 in = (void *)(mod_base + l->fileofs);
3309 if (l->filelen % sizeof(*in))
3310 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3311 count = l->filelen / sizeof(*in);
3312 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3314 loadmodel->brushq3.data_textures = out;
3315 loadmodel->brushq3.num_textures = count;
3317 for (i = 0;i < count;i++, in++, out++)
3319 strlcpy (out->name, in->name, sizeof (out->name));
3320 out->surfaceflags = LittleLong(in->surfaceflags);
3321 out->nativecontents = LittleLong(in->contents);
3322 out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents);
3323 out->renderflags = 0;
3324 if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk"))
3325 out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW;
3326 if (!strncmp(out->name, "textures/skies/", 15))
3327 out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY;
3330 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3334 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3340 in = (void *)(mod_base + l->fileofs);
3341 if (l->filelen % sizeof(*in))
3342 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3343 count = l->filelen / sizeof(*in);
3344 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3346 loadmodel->brushq3.data_planes = out;
3347 loadmodel->brushq3.num_planes = count;
3349 for (i = 0;i < count;i++, in++, out++)
3351 out->normal[0] = LittleLong(in->normal[0]);
3352 out->normal[1] = LittleLong(in->normal[1]);
3353 out->normal[2] = LittleLong(in->normal[2]);
3354 out->dist = LittleLong(in->dist);
3359 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3362 q3mbrushside_t *out;
3365 in = (void *)(mod_base + l->fileofs);
3366 if (l->filelen % sizeof(*in))
3367 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3368 count = l->filelen / sizeof(*in);
3369 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3371 loadmodel->brushq3.data_brushsides = out;
3372 loadmodel->brushq3.num_brushsides = count;
3374 for (i = 0;i < count;i++, in++, out++)
3376 n = LittleLong(in->planeindex);
3377 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3378 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3379 out->plane = loadmodel->brushq3.data_planes + n;
3380 n = LittleLong(in->textureindex);
3381 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3382 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3383 out->texture = loadmodel->brushq3.data_textures + n;
3387 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3391 int i, j, n, c, count, maxplanes;
3393 winding_t *temp1, *temp2;
3395 in = (void *)(mod_base + l->fileofs);
3396 if (l->filelen % sizeof(*in))
3397 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3398 count = l->filelen / sizeof(*in);
3399 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3401 loadmodel->brushq3.data_brushes = out;
3402 loadmodel->brushq3.num_brushes = count;
3404 temp1 = Winding_New(64);
3405 temp2 = Winding_New(64);
3410 for (i = 0;i < count;i++, in++, out++)
3412 n = LittleLong(in->firstbrushside);
3413 c = LittleLong(in->numbrushsides);
3414 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3415 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3416 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3417 out->numbrushsides = c;
3418 n = LittleLong(in->textureindex);
3419 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3420 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3421 out->texture = loadmodel->brushq3.data_textures + n;
3423 // make a list of mplane_t structs to construct a colbrush from
3424 if (maxplanes < out->numbrushsides)
3426 maxplanes = out->numbrushsides;
3429 planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes);
3431 for (j = 0;j < out->numbrushsides;j++)
3433 VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal);
3434 planes[j].dist = out->firstbrushside[j].plane->dist;
3436 // make the colbrush from the planes
3437 out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, temp1, temp2);
3441 Winding_Free(temp1);
3442 Winding_Free(temp2);
3445 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3451 in = (void *)(mod_base + l->fileofs);
3452 if (l->filelen % sizeof(*in))
3453 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3454 count = l->filelen / sizeof(*in);
3455 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3457 loadmodel->brushq3.data_effects = out;
3458 loadmodel->brushq3.num_effects = count;
3460 for (i = 0;i < count;i++, in++, out++)
3462 strlcpy (out->shadername, in->shadername, sizeof (out->shadername));
3463 n = LittleLong(in->brushindex);
3464 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3465 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3466 out->brush = loadmodel->brushq3.data_brushes + n;
3467 out->unknown = LittleLong(in->unknown);
3471 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3476 in = (void *)(mod_base + l->fileofs);
3477 if (l->filelen % sizeof(*in))
3478 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3479 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3480 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)));
3481 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + count * 3;
3482 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + count * 2;
3483 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + count * 2;
3484 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + count * 3;
3485 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + count * 3;
3486 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + count * 3;
3488 for (i = 0;i < count;i++, in++)
3490 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3491 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3492 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3493 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3494 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3495 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3496 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3497 // svector/tvector are calculated later in face loading
3498 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3499 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3500 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3501 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3502 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3503 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3504 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3505 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3506 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3507 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3508 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3509 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3510 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3514 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3520 in = (void *)(mod_base + l->fileofs);
3521 if (l->filelen % sizeof(int[3]))
3522 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3523 count = l->filelen / sizeof(*in);
3524 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out) * 2);
3526 loadmodel->brushq3.num_triangles = count / 3;
3527 loadmodel->brushq3.data_element3i = out;
3528 loadmodel->brushq3.data_neighbor3i = out + count;
3530 for (i = 0;i < count;i++, in++, out++)
3532 *out = LittleLong(*in);
3533 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3535 Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices);
3541 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3549 in = (void *)(mod_base + l->fileofs);
3550 if (l->filelen % sizeof(*in))
3551 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3552 count = l->filelen / sizeof(*in);
3553 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3555 loadmodel->brushq3.data_lightmaps = out;
3556 loadmodel->brushq3.num_lightmaps = count;
3558 for (i = 0;i < count;i++, in++, out++)
3559 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3562 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3566 int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles;
3567 //int *originalelement3i;
3568 //int *originalneighbor3i;
3569 float *originalvertex3f;
3570 //float *originalsvector3f;
3571 //float *originaltvector3f;
3572 //float *originalnormal3f;
3573 float *originalcolor4f;
3574 float *originaltexcoordtexture2f;
3575 float *originaltexcoordlightmap2f;
3578 in = (void *)(mod_base + l->fileofs);
3579 if (l->filelen % sizeof(*in))
3580 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3581 count = l->filelen / sizeof(*in);
3582 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3584 loadmodel->brushq3.data_faces = out;
3585 loadmodel->brushq3.num_faces = count;
3587 for (i = 0;i < count;i++, in++, out++)
3589 // check face type first
3590 out->type = LittleLong(in->type);
3591 if (out->type != Q3FACETYPE_POLYGON
3592 && out->type != Q3FACETYPE_PATCH
3593 && out->type != Q3FACETYPE_MESH
3594 && out->type != Q3FACETYPE_FLARE)
3596 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3597 out->num_vertices = 0;
3598 out->num_triangles = 0;
3599 out->type = 0; // error
3603 n = LittleLong(in->textureindex);
3604 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3606 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3607 out->num_vertices = 0;
3608 out->num_triangles = 0;
3609 out->type = 0; // error
3613 out->texture = loadmodel->brushq3.data_textures + n;
3614 n = LittleLong(in->effectindex);
3615 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3617 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3623 out->effect = loadmodel->brushq3.data_effects + n;
3624 n = LittleLong(in->lightmapindex);
3625 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3627 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3631 out->lightmaptexture = NULL;
3633 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3635 out->firstvertex = LittleLong(in->firstvertex);
3636 out->num_vertices = LittleLong(in->numvertices);
3637 out->firstelement = LittleLong(in->firstelement);
3638 out->num_triangles = LittleLong(in->numelements) / 3;
3639 if (out->num_triangles * 3 != LittleLong(in->numelements))
3641 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));
3642 out->num_vertices = 0;
3643 out->num_triangles = 0;
3644 out->type = 0; // error
3647 if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices)
3649 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);
3650 out->num_vertices = 0;
3651 out->num_triangles = 0;
3652 out->type = 0; // error
3655 if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3)
3657 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);
3658 out->num_vertices = 0;
3659 out->num_triangles = 0;
3660 out->type = 0; // error
3665 case Q3FACETYPE_POLYGON:
3666 case Q3FACETYPE_MESH:
3667 // no processing necessary
3668 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3669 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3670 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3671 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3672 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3673 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3674 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3675 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3676 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3678 case Q3FACETYPE_PATCH:
3679 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3680 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3681 if (patchsize[0] < 1 || patchsize[1] < 1)
3683 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3684 out->num_vertices = 0;
3685 out->num_triangles = 0;
3686 out->type = 0; // error
3689 // convert patch to Q3FACETYPE_MESH
3690 xlevel = mod_q3bsp_curves_subdivide_level.integer;
3691 ylevel = mod_q3bsp_curves_subdivide_level.integer;
3692 finalwidth = ((patchsize[0] - 1) << xlevel) + 1;
3693 finalheight = ((patchsize[1] - 1) << ylevel) + 1;
3694 finalvertices = finalwidth * finalheight;
3695 finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2;
3696 originalvertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3697 //originalsvector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3698 //originaltvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3699 //originalnormal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3700 originaltexcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3701 originaltexcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3702 originalcolor4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3703 //originalelement3i = loadmodel->brushq3.data_element3i + out->firstelement;
3704 //originalneighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3706 originalvertex3f = out->data_vertex3f;
3707 //originalsvector3f = out->data_svector3f;
3708 //originaltvector3f = out->data_tvector3f;
3709 //originalnormal3f = out->data_normal3f;
3710 originalcolor4f = out->data_color4f;
3711 originaltexcoordtexture2f = out->data_texcoordtexture2f;
3712 originaltexcoordlightmap2f = out->data_texcoordlightmap2f;
3713 //originalelement3i = out->data_element3i;
3714 //originalneighbor3i = out->data_neighbor3i;
3716 out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices);
3717 out->data_svector3f = out->data_vertex3f + finalvertices * 3;
3718 out->data_tvector3f = out->data_svector3f + finalvertices * 3;
3719 out->data_normal3f = out->data_tvector3f + finalvertices * 3;
3720 out->data_color4f = out->data_normal3f + finalvertices * 3;
3721 out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4;
3722 out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2;
3723 out->data_element3i = Mem_Alloc(loadmodel->mempool, sizeof(int[6]) * finaltriangles);
3724 out->data_neighbor3i = out->data_element3i + finaltriangles * 3;
3725 out->type = Q3FACETYPE_MESH;
3726 out->firstvertex = -1;
3727 out->num_vertices = finalvertices;
3728 out->firstelement = -1;
3729 out->num_triangles = finaltriangles;
3730 // generate geometry
3731 // (note: normals are skipped because they get recalculated)
3732 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f);
3733 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f);
3734 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f);
3735 QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f);
3736 // generate elements
3737 e = out->data_element3i;
3738 for (y = 0;y < finalheight - 1;y++)
3740 row0 = (y + 0) * finalwidth;
3741 row1 = (y + 1) * finalwidth;
3742 for (x = 0;x < finalwidth - 1;x++)
3754 out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f);
3755 if (developer.integer)
3757 if (out->num_triangles < finaltriangles)
3758 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);
3760 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);
3762 // q3map does not put in collision brushes for curves... ugh
3763 out->collisions = true;
3765 case Q3FACETYPE_FLARE:
3766 Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3768 out->num_vertices = 0;
3769 out->num_triangles = 0;
3773 for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++)
3774 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3776 if (invalidelements)
3778 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);
3779 for (j = 0;j < out->num_triangles * 3;j++)
3781 Con_Printf(" %i", out->data_element3i[j]);
3782 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices)
3783 out->data_element3i[j] = 0;
3787 // for shadow volumes
3788 Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles);
3789 // for per pixel lighting
3790 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);
3791 // calculate a bounding box
3792 VectorClear(out->mins);
3793 VectorClear(out->maxs);
3794 if (out->num_vertices)
3796 VectorCopy(out->data_vertex3f, out->mins);
3797 VectorCopy(out->data_vertex3f, out->maxs);
3798 for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3)
3800 out->mins[0] = min(out->mins[0], v[0]);
3801 out->maxs[0] = max(out->maxs[0], v[0]);
3802 out->mins[1] = min(out->mins[1], v[1]);
3803 out->maxs[1] = max(out->maxs[1], v[1]);
3804 out->mins[2] = min(out->mins[2], v[2]);
3805 out->maxs[2] = max(out->maxs[2], v[2]);
3807 out->mins[0] -= 1.0f;
3808 out->mins[1] -= 1.0f;
3809 out->mins[2] -= 1.0f;
3810 out->maxs[0] += 1.0f;
3811 out->maxs[1] += 1.0f;
3812 out->maxs[2] += 1.0f;
3816 // LordHavoc: experimental array merger (disabled because it wastes time and uses 2x memory while merging)
3819 int totalverts, totaltris;
3820 int originalnum_vertices;
3821 float *originaldata_vertex3f;
3822 float *originaldata_texcoordtexture2f;
3823 float *originaldata_texcoordlightmap2f;
3824 float *originaldata_svector3f;
3825 float *originaldata_tvector3f;
3826 float *originaldata_normal3f;
3827 float *originaldata_color4f;
3828 int originalnum_triangles;
3829 int *originaldata_element3i;
3830 int *originaldata_neighbor3i;
3834 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
3838 totalverts += out->num_vertices;
3839 totaltris += out->num_triangles;
3842 originalnum_vertices = loadmodel->brushq3.num_vertices;
3843 originaldata_vertex3f = loadmodel->brushq3.data_vertex3f;
3844 originaldata_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f;
3845 originaldata_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f;
3846 originaldata_svector3f = loadmodel->brushq3.data_svector3f;
3847 originaldata_tvector3f = loadmodel->brushq3.data_tvector3f;
3848 originaldata_normal3f = loadmodel->brushq3.data_normal3f;
3849 originaldata_color4f = loadmodel->brushq3.data_color4f;
3850 originalnum_triangles = loadmodel->brushq3.num_triangles;
3851 originaldata_element3i = loadmodel->brushq3.data_element3i;
3852 originaldata_neighbor3i = loadmodel->brushq3.data_neighbor3i;
3853 loadmodel->brushq3.num_vertices = totalverts;
3854 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, totalverts * (sizeof(float) * (3 + 2 + 2 + 3 + 3 + 3 + 4)) + totaltris * (sizeof(int) * (3 * 2)));
3855 loadmodel->brushq3.data_texcoordtexture2f = loadmodel->brushq3.data_vertex3f + totalverts * 3;
3856 loadmodel->brushq3.data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2;
3857 loadmodel->brushq3.data_svector3f = loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2;
3858 loadmodel->brushq3.data_tvector3f = loadmodel->brushq3.data_svector3f + totalverts * 3;
3859 loadmodel->brushq3.data_normal3f = loadmodel->brushq3.data_tvector3f + totalverts * 3;
3860 loadmodel->brushq3.data_color4f = loadmodel->brushq3.data_normal3f + totalverts * 3;
3861 loadmodel->brushq3.num_triangles = totaltris;
3862 loadmodel->brushq3.data_element3i = (int *)(loadmodel->brushq3.data_color4f + totalverts * 4);
3863 loadmodel->brushq3.data_neighbor3i = loadmodel->brushq3.data_element3i + totaltris * 3;
3866 for (i = 0, out = loadmodel->brushq3.data_faces;i < count;i++, out++)
3870 Con_Printf("totalverts %i, totaltris %i\n", totalverts, totaltris);
3871 memcpy(loadmodel->brushq3.data_vertex3f + totalverts * 3, out->data_vertex3f, out->num_vertices * 3 * sizeof(float));
3872 memcpy(loadmodel->brushq3.data_texcoordtexture2f + totalverts * 2, out->data_texcoordtexture2f, out->num_vertices * 2 * sizeof(float));
3873 memcpy(loadmodel->brushq3.data_texcoordlightmap2f + totalverts * 2, out->data_texcoordlightmap2f, out->num_vertices * 2 * sizeof(float));
3874 memcpy(loadmodel->brushq3.data_svector3f + totalverts * 3, out->data_svector3f, out->num_vertices * 3 * sizeof(float));
3875 memcpy(loadmodel->brushq3.data_tvector3f + totalverts * 3, out->data_tvector3f, out->num_vertices * 3 * sizeof(float));
3876 memcpy(loadmodel->brushq3.data_normal3f + totalverts * 3, out->data_normal3f, out->num_vertices * 3 * sizeof(float));
3877 memcpy(loadmodel->brushq3.data_color4f + totalverts * 4, out->data_color4f, out->num_vertices * 4 * sizeof(float));
3878 memcpy(loadmodel->brushq3.data_element3i + totaltris * 3, out->data_element3i, out->num_triangles * 3 * sizeof(int));
3879 memcpy(loadmodel->brushq3.data_neighbor3i + totaltris * 3, out->data_neighbor3i, out->num_triangles * 3 * sizeof(int));
3880 if (out->firstvertex == -1)
3881 Mem_Free(out->data_vertex3f);
3882 if (out->firstelement == -1)
3883 Mem_Free(out->data_element3i);
3884 out->firstvertex = totalverts;
3885 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3886 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3887 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3888 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3889 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3890 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3891 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3892 out->firstelement = totaltris * 3;
3893 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3894 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3895 //for (j = 0;j < out->numtriangles * 3;j++)
3896 // out->data_element3i[j] += totalverts - out->firstvertex;
3897 totalverts += out->num_vertices;
3898 totaltris += out->num_triangles;
3900 Mem_Free(originaldata_vertex3f);
3901 Mem_Free(originaldata_element3i);
3906 static void Mod_Q3BSP_LoadModels(lump_t *l)
3910 int i, j, n, c, count;
3912 in = (void *)(mod_base + l->fileofs);
3913 if (l->filelen % sizeof(*in))
3914 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3915 count = l->filelen / sizeof(*in);
3916 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3918 loadmodel->brushq3.data_models = out;
3919 loadmodel->brushq3.num_models = count;
3921 for (i = 0;i < count;i++, in++, out++)
3923 for (j = 0;j < 3;j++)
3925 out->mins[j] = LittleFloat(in->mins[j]);
3926 out->maxs[j] = LittleFloat(in->maxs[j]);
3928 n = LittleLong(in->firstface);
3929 c = LittleLong(in->numfaces);
3930 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3931 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3932 out->firstface = loadmodel->brushq3.data_faces + n;
3934 n = LittleLong(in->firstbrush);
3935 c = LittleLong(in->numbrushes);
3936 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3937 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3938 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3939 out->numbrushes = c;
3943 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3949 in = (void *)(mod_base + l->fileofs);
3950 if (l->filelen % sizeof(*in))
3951 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3952 count = l->filelen / sizeof(*in);
3953 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3955 loadmodel->brushq3.data_leafbrushes = out;
3956 loadmodel->brushq3.num_leafbrushes = count;
3958 for (i = 0;i < count;i++, in++, out++)
3960 n = LittleLong(*in);
3961 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3962 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3963 *out = loadmodel->brushq3.data_brushes + n;
3967 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3973 in = (void *)(mod_base + l->fileofs);
3974 if (l->filelen % sizeof(*in))
3975 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3976 count = l->filelen / sizeof(*in);
3977 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3979 loadmodel->brushq3.data_leaffaces = out;
3980 loadmodel->brushq3.num_leaffaces = count;
3982 for (i = 0;i < count;i++, in++, out++)
3984 n = LittleLong(*in);
3985 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3986 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3987 *out = loadmodel->brushq3.data_faces + n;
3991 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3995 int i, j, n, c, count;
3997 in = (void *)(mod_base + l->fileofs);
3998 if (l->filelen % sizeof(*in))
3999 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4000 count = l->filelen / sizeof(*in);
4001 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4003 loadmodel->brushq3.data_leafs = out;
4004 loadmodel->brushq3.num_leafs = count;
4006 for (i = 0;i < count;i++, in++, out++)
4008 out->isnode = false;
4010 out->clusterindex = LittleLong(in->clusterindex);
4011 out->areaindex = LittleLong(in->areaindex);
4012 for (j = 0;j < 3;j++)
4014 // yes the mins/maxs are ints
4015 out->mins[j] = LittleLong(in->mins[j]);
4016 out->maxs[j] = LittleLong(in->maxs[j]);
4018 n = LittleLong(in->firstleafface);
4019 c = LittleLong(in->numleaffaces);
4020 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4021 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4022 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4023 out->numleaffaces = c;
4024 n = LittleLong(in->firstleafbrush);
4025 c = LittleLong(in->numleafbrushes);
4026 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4027 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4028 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4029 out->numleafbrushes = c;
4033 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4036 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4037 node->parent = parent;
4040 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4041 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4045 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4051 in = (void *)(mod_base + l->fileofs);
4052 if (l->filelen % sizeof(*in))
4053 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4054 count = l->filelen / sizeof(*in);
4055 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4057 loadmodel->brushq3.data_nodes = out;
4058 loadmodel->brushq3.num_nodes = count;
4060 for (i = 0;i < count;i++, in++, out++)
4064 n = LittleLong(in->planeindex);
4065 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4066 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4067 out->plane = loadmodel->brushq3.data_planes + n;
4068 for (j = 0;j < 2;j++)
4070 n = LittleLong(in->childrenindex[j]);
4073 if (n >= loadmodel->brushq3.num_nodes)
4074 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4075 out->children[j] = loadmodel->brushq3.data_nodes + n;
4080 if (n >= loadmodel->brushq3.num_leafs)
4081 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4082 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4085 for (j = 0;j < 3;j++)
4087 // yes the mins/maxs are ints
4088 out->mins[j] = LittleLong(in->mins[j]);
4089 out->maxs[j] = LittleLong(in->maxs[j]);
4093 // set the parent pointers
4094 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4097 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4100 q3dlightgrid_t *out;
4103 if (l->filelen == 0)
4106 in = (void *)(mod_base + l->fileofs);
4107 if (l->filelen % sizeof(*in))
4108 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4109 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4110 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4111 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4112 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4113 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4114 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4115 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4116 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4117 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4118 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4119 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4120 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4121 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4122 if (l->filelen < count * (int)sizeof(*in))
4123 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]);
4124 if (l->filelen != count * (int)sizeof(*in))
4125 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4127 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4128 loadmodel->brushq3.data_lightgrid = out;
4129 loadmodel->brushq3.num_lightgrid = count;
4131 // no swapping or validation necessary
4132 memcpy(out, in, count * (int)sizeof(*out));
4134 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]);
4135 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]);
4138 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4143 if (l->filelen == 0)
4146 in = (void *)(mod_base + l->fileofs);
4148 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4150 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4151 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4152 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4153 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4154 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4155 if (l->filelen < totalchains + (int)sizeof(*in))
4156 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);
4158 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4159 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4162 static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4164 // FIXME: finish this code
4165 VectorCopy(in, out);
4168 static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4170 int i, j, k, index[3];
4171 float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch;
4172 q3dlightgrid_t *a, *s;
4173 // FIXME: write this
4174 if (!model->brushq3.num_lightgrid)
4176 ambientcolor[0] += 128;
4177 ambientcolor[1] += 128;
4178 ambientcolor[2] += 128;
4181 Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed);
4182 //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld);
4183 //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]);
4184 transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1);
4185 transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1);
4186 transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1);
4187 index[0] = (int)floor(transformed[0]);
4188 index[1] = (int)floor(transformed[1]);
4189 index[2] = (int)floor(transformed[2]);
4190 //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]);
4191 // now lerp the values
4192 VectorClear(diffusenormal);
4193 a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]];
4194 for (k = 0;k < 2;k++)
4196 blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2])));
4197 if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2])
4199 for (j = 0;j < 2;j++)
4201 blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1])));
4202 if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1])
4204 for (i = 0;i < 2;i++)
4206 blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0])));
4207 if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0])
4209 s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i;
4210 VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor);
4211 VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor);
4212 pitch = s->diffusepitch * M_PI / 128;
4213 yaw = s->diffuseyaw * M_PI / 128;
4214 sinpitch = sin(pitch);
4215 diffusenormal[0] += blend * (cos(yaw) * sinpitch);
4216 diffusenormal[1] += blend * (sin(yaw) * sinpitch);
4217 diffusenormal[2] += blend * (cos(pitch));
4218 //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)));
4222 VectorNormalize(diffusenormal);
4223 //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]);
4226 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)
4228 int i, startside, endside;
4229 float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3];
4233 if (startfrac >= trace->fraction)
4235 // note: all line fragments past first impact fraction are ignored
4236 while (node->isnode)
4238 // recurse down node sides
4239 dist1 = PlaneDiff(start, node->plane);
4240 dist2 = PlaneDiff(end, node->plane);
4241 startside = dist1 < 0;
4242 endside = dist2 < 0;
4243 if (startside == endside)
4245 // most of the time the line fragment is on one side of the plane
4246 node = node->children[startside];
4250 // line crosses node plane, split the line
4251 midfrac = dist1 / (dist1 - dist2);
4252 VectorLerp(linestart, midfrac, lineend, mid);
4253 // take the near side first
4254 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe);
4255 if (midfrac < trace->fraction)
4256 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe);
4261 segmentmins[0] = min(start[0], end[0]);
4262 segmentmins[1] = min(start[1], end[1]);
4263 segmentmins[2] = min(start[2], end[2]);
4264 segmentmaxs[0] = max(start[0], end[0]);
4265 segmentmaxs[1] = max(start[1], end[1]);
4266 segmentmaxs[2] = max(start[2], end[2]);
4267 leaf = (q3mleaf_t *)node;
4268 for (i = 0;i < leaf->numleafbrushes;i++)
4270 if (startfrac >= trace->fraction)
4272 brush = leaf->firstleafbrush[i]->colbrushf;
4273 if (brush && brush->markframe != markframe)
4275 brush->markframe = markframe;
4276 if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs))
4277 Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4280 if (mod_q3bsp_curves_collisions.integer)
4282 for (i = 0;i < leaf->numleaffaces;i++)
4284 if (startfrac >= trace->fraction)
4286 face = leaf->firstleafface[i];
4287 if (face->collisions && face->collisionmarkframe != markframe)
4289 face->collisionmarkframe = markframe;
4290 if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs))
4291 Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4297 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)
4300 float nodesegmentmins[3], nodesegmentmaxs[3];
4304 nodesegmentmins[0] = max(segmentmins[0], node->mins[0]);
4305 nodesegmentmins[1] = max(segmentmins[1], node->mins[1]);
4306 nodesegmentmins[2] = max(segmentmins[2], node->mins[2]);
4307 nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]);
4308 nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]);
4309 nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]);
4310 if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2])
4314 // recurse down node sides
4315 sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane);
4318 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4319 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4321 else if (sides == 2)
4322 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4324 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs);
4326 dist = node->plane->dist - (1.0f / 8.0f);
4327 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4329 if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist)
4331 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4337 dist = node->plane->dist + (1.0f / 8.0f);
4338 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4340 if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist)
4342 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs);
4348 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4350 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4352 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4358 leaf = (q3mleaf_t *)node;
4359 for (i = 0;i < leaf->numleafbrushes;i++)
4361 brush = leaf->firstleafbrush[i]->colbrushf;
4362 if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs))
4364 brush->markframe = markframe;
4365 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4368 if (mod_q3bsp_curves_collisions.integer)
4370 for (i = 0;i < leaf->numleaffaces;i++)
4372 face = leaf->firstleafface[i];
4373 // 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
4374 if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs))
4375 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4381 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)
4384 float segmentmins[3], segmentmaxs[3];
4385 colbrushf_t *thisbrush_start, *thisbrush_end;
4386 matrix4x4_t startmatrix, endmatrix;
4387 static int markframe = 0;
4389 memset(trace, 0, sizeof(*trace));
4390 trace->fraction = 1;
4391 trace->hitsupercontentsmask = hitsupercontentsmask;
4392 Matrix4x4_CreateIdentity(&startmatrix);
4393 Matrix4x4_CreateIdentity(&endmatrix);
4394 segmentmins[0] = min(boxstartmins[0], boxendmins[0]);
4395 segmentmins[1] = min(boxstartmins[1], boxendmins[1]);
4396 segmentmins[2] = min(boxstartmins[2], boxendmins[2]);
4397 segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]);
4398 segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]);
4399 segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]);
4400 if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs))
4403 if (model->brushq3.submodel)
4405 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4406 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4407 Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4408 if (mod_q3bsp_curves_collisions.integer)
4410 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4412 face = model->brushq3.data_thismodel->firstface + i;
4413 if (face->collisions)
4414 Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4419 Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe);
4423 // box trace, performed as brush trace
4424 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4425 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4426 if (model->brushq3.submodel)
4428 for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++)
4429 if (model->brushq3.data_thismodel->firstbrush[i].colbrushf)
4430 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf);
4431 if (mod_q3bsp_curves_collisions.integer)
4433 for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++)
4435 face = model->brushq3.data_thismodel->firstface + i;
4436 if (face->collisions)
4437 Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs);
4442 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs);
4447 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)
4454 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4455 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4458 // node - recurse down the BSP tree
4459 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4462 node = node->children[0];
4465 node = node->children[1];
4467 default: // crossing
4468 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4470 node = node->children[1];
4477 static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4479 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4482 //Returns PVS data for a given point
4483 //(note: can return NULL)
4484 static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p)
4487 Mod_CheckLoaded(model);
4488 node = model->brushq3.data_nodes;
4489 while (node->isnode)
4490 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
4491 if (((q3mleaf_t *)node)->clusterindex >= 0)
4492 return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4497 static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node)
4503 while (node->isnode)
4505 d = PlaneDiff(org, node->plane);
4507 node = node->children[0];
4508 else if (d < -radius)
4509 node = node->children[1];
4512 // go down both sides
4513 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
4514 node = node->children[1];
4517 // if this is a leaf with a pvs, accumulate the pvs bits
4518 if (((q3mleaf_t *)node)->clusterindex >= 0)
4520 pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength;
4521 for (i = 0;i < pvsbytes;i++)
4522 pvsbuffer[i] |= pvs[i];
4527 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
4528 //of the given point.
4529 static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4531 int bytes = model->brushq3.num_pvschainlength;
4532 bytes = min(bytes, pvsbufferlength);
4533 memset(pvsbuffer, 0, bytes);
4534 Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes);
4539 static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents)
4541 int supercontents = 0;
4542 if (nativecontents & Q2CONTENTS_SOLID)
4543 supercontents |= SUPERCONTENTS_SOLID;
4544 if (nativecontents & Q2CONTENTS_WATER)
4545 supercontents |= SUPERCONTENTS_WATER;
4546 if (nativecontents & Q2CONTENTS_SLIME)
4547 supercontents |= SUPERCONTENTS_SLIME;
4548 if (nativecontents & Q2CONTENTS_LAVA)
4549 supercontents |= SUPERCONTENTS_LAVA;
4550 return supercontents;
4553 static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents)
4555 int nativecontents = 0;
4556 if (supercontents & SUPERCONTENTS_SOLID)
4557 nativecontents |= Q2CONTENTS_SOLID;
4558 if (supercontents & SUPERCONTENTS_WATER)
4559 nativecontents |= Q2CONTENTS_WATER;
4560 if (supercontents & SUPERCONTENTS_SLIME)
4561 nativecontents |= Q2CONTENTS_SLIME;
4562 if (supercontents & SUPERCONTENTS_LAVA)
4563 nativecontents |= Q2CONTENTS_LAVA;
4564 return nativecontents;
4567 extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4568 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4569 extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4570 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);
4571 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4574 q3dheader_t *header;
4575 float corner[3], yawradius, modelradius;
4577 mod->type = mod_brushq3;
4581 header = (q3dheader_t *)buffer;
4583 i = LittleLong(header->version);
4584 if (i != Q3BSPVERSION)
4585 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4586 if (loadmodel->isworldmodel)
4588 Cvar_SetValue("halflifebsp", false);
4589 // until we get a texture for it...
4593 mod->soundfromcenter = true;
4594 mod->TraceBox = Mod_Q3BSP_TraceBox;
4595 mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
4596 mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
4597 mod->brush.GetPVS = Mod_Q3BSP_GetPVS;
4598 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4599 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4600 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4601 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4602 //mod->DrawSky = R_Q3BSP_DrawSky;
4603 mod->Draw = R_Q3BSP_Draw;
4604 mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4605 mod->DrawLight = R_Q3BSP_DrawLight;
4607 mod_base = (qbyte *)header;
4609 // swap all the lumps
4610 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4611 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4613 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4614 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4615 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4616 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4617 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4618 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4619 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4620 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4621 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4622 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4623 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4624 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4625 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4626 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4627 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4628 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4629 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4630 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4632 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4639 // LordHavoc: only register submodels if it is the world
4640 // (prevents bsp models from replacing world submodels)
4641 if (!loadmodel->isworldmodel)
4643 // duplicate the basic information
4644 sprintf(name, "*%i", i);
4645 mod = Mod_FindName(name);
4647 strcpy(mod->name, name);
4648 // textures and memory belong to the main model
4649 mod->texturepool = NULL;
4650 mod->mempool = NULL;
4652 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4653 mod->brushq3.submodel = i;
4655 VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins);
4656 VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs);
4657 corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0]));
4658 corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1]));
4659 corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2]));
4660 modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]);
4661 yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]);
4662 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
4663 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
4664 mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius;
4665 mod->yawmins[0] = mod->yawmins[1] = -yawradius;
4666 mod->yawmins[2] = mod->normalmins[2];
4667 mod->yawmaxs[2] = mod->normalmaxs[2];
4668 mod->radius = modelradius;
4669 mod->radius2 = modelradius * modelradius;
4671 for (j = 0;j < mod->brushq3.data_thismodel->numfaces;j++)
4672 if (mod->brushq3.data_thismodel->firstface[j].texture->renderflags & Q3MTEXTURERENDERFLAGS_SKY)
4674 if (j < mod->brushq3.data_thismodel->numfaces)
4675 mod->DrawSky = R_Q3BSP_DrawSky;
4679 void Mod_IBSP_Load(model_t *mod, void *buffer)
4681 int i = LittleLong(((int *)buffer)[1]);
4682 if (i == Q3BSPVERSION)
4683 Mod_Q3BSP_Load(mod,buffer);
4684 else if (i == Q2BSPVERSION)
4685 Mod_Q2BSP_Load(mod,buffer);
4687 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4690 void Mod_MAP_Load(model_t *mod, void *buffer)
4692 Host_Error("Mod_MAP_Load: not yet implemented\n");