X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=model_brush.c;h=55a33a5371e460c3f8f0e28ef84181e19d0ec242;hp=b5bc85e1b14248e83d2a4e3394ed08bce28734dc;hb=af0b7a1fa11c2ceedeb9f66ca950f6c0bff8ad8f;hpb=2dbb3966a2791390e57a081d68238788cbeb4a3c diff --git a/model_brush.c b/model_brush.c index b5bc85e1..55a33a53 100644 --- a/model_brush.c +++ b/model_brush.c @@ -19,10 +19,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "quakedef.h" +#include "image.h" +#include "r_shadow.h" +#include "winding.h" +#include "curves.h" // note: model_shared.c sets up r_notexture, and r_surf_notexture -qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8]; +qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8]; //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"}; cvar_t halflifebsp = {0, "halflifebsp", "0"}; @@ -30,18 +34,11 @@ cvar_t r_novis = {0, "r_novis", "0"}; cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"}; cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"}; cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"}; -cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"}; +cvar_t mod_q3bsp_curves_subdivide_level = {0, "mod_q3bsp_curves_subdivide_level", "2"}; +cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1"}; +cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1"}; -#define NUM_DETAILTEXTURES 1 -static rtexture_t *detailtextures[NUM_DETAILTEXTURES]; -static rtexturepool_t *detailtexturepool; - -/* -=============== -Mod_BrushInit -=============== -*/ -void Mod_BrushInit (void) +void Mod_BrushInit(void) { // Cvar_RegisterVariable(&r_subdivide_size); Cvar_RegisterVariable(&halflifebsp); @@ -49,65 +46,13 @@ void Mod_BrushInit (void) Cvar_RegisterVariable(&r_miplightmaps); Cvar_RegisterVariable(&r_lightmaprgba); Cvar_RegisterVariable(&r_nosurftextures); - Cvar_RegisterVariable(&r_sortsurfaces); - memset(mod_novis, 0xff, sizeof(mod_novis)); -} - -void Mod_BrushStartup (void) -{ - int i, x, y, light; - float vc[3], vx[3], vy[3], vn[3], lightdir[3]; -#define DETAILRESOLUTION 256 - qbyte data[DETAILRESOLUTION][DETAILRESOLUTION][4], noise[DETAILRESOLUTION][DETAILRESOLUTION]; - detailtexturepool = R_AllocTexturePool(); - lightdir[0] = 0.5; - lightdir[1] = 1; - lightdir[2] = -0.25; - VectorNormalize(lightdir); - for (i = 0;i < NUM_DETAILTEXTURES;i++) - { - fractalnoise(&noise[0][0], DETAILRESOLUTION, DETAILRESOLUTION >> 4); - for (y = 0;y < DETAILRESOLUTION;y++) - { - for (x = 0;x < DETAILRESOLUTION;x++) - { - vc[0] = x; - vc[1] = y; - vc[2] = noise[y][x] * (1.0f / 32.0f); - vx[0] = x + 1; - vx[1] = y; - vx[2] = noise[y][(x + 1) % DETAILRESOLUTION] * (1.0f / 32.0f); - vy[0] = x; - vy[1] = y + 1; - vy[2] = noise[(y + 1) % DETAILRESOLUTION][x] * (1.0f / 32.0f); - VectorSubtract(vx, vc, vx); - VectorSubtract(vy, vc, vy); - CrossProduct(vx, vy, vn); - VectorNormalize(vn); - light = 128 - DotProduct(vn, lightdir) * 128; - light = bound(0, light, 255); - data[y][x][0] = data[y][x][1] = data[y][x][2] = light; - data[y][x][3] = 255; - } - } - detailtextures[i] = R_LoadTexture(detailtexturepool, va("detailtexture%i", i), DETAILRESOLUTION, DETAILRESOLUTION, &data[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_PRECACHE); - } -} - -void Mod_BrushShutdown (void) -{ - int i; - for (i = 0;i < NUM_DETAILTEXTURES;i++) - R_FreeTexture(detailtextures[i]); - R_FreeTexturePool(&detailtexturepool); + Cvar_RegisterVariable(&mod_q3bsp_curves_subdivide_level); + Cvar_RegisterVariable(&mod_q3bsp_curves_collisions); + Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline); + memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis)); } -/* -=============== -Mod_PointInLeaf -=============== -*/ -mleaf_t *Mod_PointInLeaf (const vec3_t p, model_t *model) +static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p) { mnode_t *node; @@ -118,14 +63,73 @@ mleaf_t *Mod_PointInLeaf (const vec3_t p, model_t *model) // LordHavoc: modified to start at first clip node, // in other words: first node of the (sub)model - node = model->nodes + model->hulls[0].firstclipnode; + node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode; while (node->contents == 0) - node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist]; + node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist]; return (mleaf_t *)node; } -int Mod_PointContents (const vec3_t p, model_t *model) +static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize) +{ + int i; + mleaf_t *leaf; + leaf = Mod_Q1BSP_PointInLeaf(model, p); + if (leaf) + { + i = min(outsize, (int)sizeof(leaf->ambient_sound_level));; + if (i) + { + memcpy(out, leaf->ambient_sound_level, i); + out += i; + outsize -= i; + } + } + if (outsize) + memset(out, 0, outsize); +} + + +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) +{ + int leafnum; +loc0: + if (node->contents < 0) + { + // leaf + if (node->contents == CONTENTS_SOLID) + return false; + leafnum = (mleaf_t *)node - model->brushq1.leafs - 1; + return pvs[leafnum >> 3] & (1 << (leafnum & 7)); + } + + // node - recurse down the BSP tree + switch (BoxOnPlaneSide(mins, maxs, node->plane)) + { + case 1: // front + node = node->children[0]; + goto loc0; + case 2: // back + node = node->children[1]; + goto loc0; + default: // crossing + if (node->children[0]->contents != CONTENTS_SOLID) + if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs)) + return true; + node = node->children[1]; + goto loc0; + } + // never reached + return false; +} + +int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) +{ + return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs); +} + +/* +static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p) { mnode_t *node; @@ -136,111 +140,635 @@ int Mod_PointContents (const vec3_t p, model_t *model) // LordHavoc: modified to start at first clip node, // in other words: first node of the (sub)model - node = model->nodes + model->hulls[0].firstclipnode; + node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode; while (node->contents == 0) - node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist]; + node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist]; return ((mleaf_t *)node)->contents; } +*/ -void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod) +typedef struct findnonsolidlocationinfo_s { - if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return; - pos[0]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return; - pos[0]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return; - pos[0]-=1; - pos[1]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return; - pos[1]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return; - pos[1]-=1; - pos[2]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return; - pos[2]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return; - pos[2]-=1; + vec3_t center; + vec_t radius; + vec3_t nudge; + vec_t bestdist; + model_t *model; } +findnonsolidlocationinfo_t; - -/* -=================== -Mod_DecompressVis -=================== -*/ -static qbyte *Mod_DecompressVis (qbyte *in, model_t *model) +#if 0 +extern cvar_t samelevel; +#endif +static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf) { - static qbyte decompressed[MAX_MAP_LEAFS/8]; - int c; - qbyte *out; - int row; + int i, surfnum, k, *tri, *mark; + float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3]; +#if 0 + float surfnormal[3]; +#endif + msurface_t *surf; + for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++) + { + surf = info->model->brushq1.surfaces + *mark; + if (surf->flags & SURF_SOLIDCLIP) + { +#if 0 + VectorCopy(surf->plane->normal, surfnormal); + if (surf->flags & SURF_PLANEBACK) + VectorNegate(surfnormal, surfnormal); +#endif + for (k = 0;k < surf->mesh.num_triangles;k++) + { + tri = surf->mesh.data_element3i + k * 3; + VectorCopy((surf->mesh.data_vertex3f + tri[0] * 3), vert[0]); + VectorCopy((surf->mesh.data_vertex3f + tri[1] * 3), vert[1]); + VectorCopy((surf->mesh.data_vertex3f + tri[2] * 3), vert[2]); + VectorSubtract(vert[1], vert[0], edge[0]); + VectorSubtract(vert[2], vert[1], edge[1]); + CrossProduct(edge[1], edge[0], facenormal); + if (facenormal[0] || facenormal[1] || facenormal[2]) + { + VectorNormalize(facenormal); +#if 0 + if (VectorDistance(facenormal, surfnormal) > 0.01f) + Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]); +#endif + f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal); + if (f <= info->bestdist && f >= -info->bestdist) + { + VectorSubtract(vert[0], vert[2], edge[2]); + VectorNormalize(edge[0]); + VectorNormalize(edge[1]); + VectorNormalize(edge[2]); + CrossProduct(facenormal, edge[0], edgenormal[0]); + CrossProduct(facenormal, edge[1], edgenormal[1]); + CrossProduct(facenormal, edge[2], edgenormal[2]); +#if 0 + if (samelevel.integer & 1) + VectorNegate(edgenormal[0], edgenormal[0]); + if (samelevel.integer & 2) + VectorNegate(edgenormal[1], edgenormal[1]); + if (samelevel.integer & 4) + VectorNegate(edgenormal[2], edgenormal[2]); + for (i = 0;i < 3;i++) + if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f + || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f + || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f) + 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]); +#endif + // face distance + if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0]) + && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1]) + && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2])) + { + // we got lucky, the center is within the face + dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal); + if (dist < 0) + { + dist = -dist; + if (info->bestdist > dist) + { + info->bestdist = dist; + VectorScale(facenormal, (info->radius - -dist), info->nudge); + } + } + else + { + if (info->bestdist > dist) + { + info->bestdist = dist; + VectorScale(facenormal, (info->radius - dist), info->nudge); + } + } + } + else + { + // check which edge or vertex the center is nearest + for (i = 0;i < 3;i++) + { + f = DotProduct(info->center, edge[i]); + if (f >= DotProduct(vert[0], edge[i]) + && f <= DotProduct(vert[1], edge[i])) + { + // on edge + VectorMA(info->center, -f, edge[i], point); + dist = sqrt(DotProduct(point, point)); + if (info->bestdist > dist) + { + info->bestdist = dist; + VectorScale(point, (info->radius / dist), info->nudge); + } + // skip both vertex checks + // (both are further away than this edge) + i++; + } + else + { + // not on edge, check first vertex of edge + VectorSubtract(info->center, vert[i], point); + dist = sqrt(DotProduct(point, point)); + if (info->bestdist > dist) + { + info->bestdist = dist; + VectorScale(point, (info->radius / dist), info->nudge); + } + } + } + } + } + } + } + } + } +} - row = (model->numleafs+7)>>3; - out = decompressed; +static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node) +{ + if (node->contents) + { + if (((mleaf_t *)node)->nummarksurfaces) + Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node); + } + else + { + float f = PlaneDiff(info->center, node->plane); + if (f >= -info->bestdist) + Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]); + if (f <= info->bestdist) + Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]); + } +} +static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius) +{ + int i; + findnonsolidlocationinfo_t info; + if (model == NULL) + { + VectorCopy(in, out); + return; + } + VectorCopy(in, info.center); + info.radius = radius; + info.model = model; + i = 0; do { - if (*in) + VectorClear(info.nudge); + info.bestdist = radius; + Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode); + VectorAdd(info.center, info.nudge, info.center); + } + while (info.bestdist < radius && ++i < 10); + VectorCopy(info.center, out); +} + +int Mod_Q1BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents) +{ + switch(nativecontents) + { + case CONTENTS_EMPTY: + return 0; + case CONTENTS_SOLID: + return SUPERCONTENTS_SOLID; + case CONTENTS_WATER: + return SUPERCONTENTS_WATER; + case CONTENTS_SLIME: + return SUPERCONTENTS_SLIME; + case CONTENTS_LAVA: + return SUPERCONTENTS_LAVA; + case CONTENTS_SKY: + return SUPERCONTENTS_SKY; + } + return 0; +} + +int Mod_Q1BSP_NativeContentsFromSuperContents(model_t *model, int supercontents) +{ + if (supercontents & SUPERCONTENTS_SOLID) + return CONTENTS_SOLID; + if (supercontents & SUPERCONTENTS_SKY) + return CONTENTS_SKY; + if (supercontents & SUPERCONTENTS_LAVA) + return CONTENTS_LAVA; + if (supercontents & SUPERCONTENTS_SLIME) + return CONTENTS_SLIME; + if (supercontents & SUPERCONTENTS_WATER) + return CONTENTS_WATER; + return CONTENTS_EMPTY; +} + +typedef struct +{ + // the hull we're tracing through + const hull_t *hull; + + // the trace structure to fill in + trace_t *trace; + + // start, end, and end - start (in model space) + double start[3]; + double end[3]; + double dist[3]; +} +RecursiveHullCheckTraceInfo_t; + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +#define HULLCHECKSTATE_EMPTY 0 +#define HULLCHECKSTATE_SOLID 1 +#define HULLCHECKSTATE_DONE 2 + +static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3]) +{ + // status variables, these don't need to be saved on the stack when + // recursing... but are because this should be thread-safe + // (note: tracing against a bbox is not thread-safe, yet) + int ret; + mplane_t *plane; + double t1, t2; + + // variables that need to be stored on the stack when recursing + dclipnode_t *node; + int side; + double midf, mid[3]; + + // LordHavoc: a goto! everyone flee in terror... :) +loc0: + // check for empty + if (num < 0) + { + num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num); + if (!t->trace->startfound) { - *out++ = *in++; - continue; + t->trace->startfound = true; + t->trace->startsupercontents |= num; + } + if (num & SUPERCONTENTS_LIQUIDSMASK) + t->trace->inwater = true; + if (num == 0) + t->trace->inopen = true; + if (num & t->trace->hitsupercontentsmask) + { + // if the first leaf is solid, set startsolid + if (t->trace->allsolid) + t->trace->startsolid = true; + return HULLCHECKSTATE_SOLID; + } + else + { + t->trace->allsolid = false; + return HULLCHECKSTATE_EMPTY; } + } + + // find the point distances + node = t->hull->clipnodes + num; + + plane = t->hull->planes + node->planenum; + if (plane->type < 3) + { + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; + } + else + { + t1 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (plane->normal, p2) - plane->dist; + } - c = in[1]; - in += 2; - while (c) + if (t1 < 0) + { + if (t2 < 0) + { + num = node->children[1]; + goto loc0; + } + side = 1; + } + else + { + if (t2 >= 0) { - *out++ = 0; - c--; + num = node->children[0]; + goto loc0; } - } while (out - decompressed < row); + side = 0; + } + + // the line intersects, find intersection point + // LordHavoc: this uses the original trace for maximum accuracy + if (plane->type < 3) + { + t1 = t->start[plane->type] - plane->dist; + t2 = t->end[plane->type] - plane->dist; + } + else + { + t1 = DotProduct (plane->normal, t->start) - plane->dist; + t2 = DotProduct (plane->normal, t->end) - plane->dist; + } + + midf = t1 / (t1 - t2); + midf = bound(p1f, midf, p2f); + VectorMA(t->start, midf, t->dist, mid); + + // recurse both sides, front side first + ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid); + // if this side is not empty, return what it is (solid or done) + if (ret != HULLCHECKSTATE_EMPTY) + return ret; - return decompressed; + ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2); + // if other side is not solid, return what it is (empty or done) + if (ret != HULLCHECKSTATE_SOLID) + return ret; + + // front is air and back is solid, this is the impact point... + if (side) + { + t->trace->plane.dist = -plane->dist; + VectorNegate (plane->normal, t->trace->plane.normal); + } + else + { + t->trace->plane.dist = plane->dist; + VectorCopy (plane->normal, t->trace->plane.normal); + } + + // bias away from surface a bit + t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON); + t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON); + + midf = t1 / (t1 - t2); + t->trace->fraction = bound(0.0f, midf, 1.0); + + return HULLCHECKSTATE_DONE; } -qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) +static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask) { - if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL) - return mod_novis; - return Mod_DecompressVis (leaf->compressed_vis, model); + // this function currently only supports same size start and end + double boxsize[3]; + RecursiveHullCheckTraceInfo_t rhc; + + memset(&rhc, 0, sizeof(rhc)); + memset(trace, 0, sizeof(trace_t)); + rhc.trace = trace; + rhc.trace->hitsupercontentsmask = hitsupercontentsmask; + rhc.trace->fraction = 1; + rhc.trace->allsolid = true; + VectorSubtract(boxstartmaxs, boxstartmins, boxsize); + if (boxsize[0] < 3) + rhc.hull = &model->brushq1.hulls[0]; // 0x0x0 + else if (model->brush.ishlbsp) + { + if (boxsize[0] <= 32) + { + if (boxsize[2] < 54) // pick the nearest of 36 or 72 + rhc.hull = &model->brushq1.hulls[3]; // 32x32x36 + else + rhc.hull = &model->brushq1.hulls[1]; // 32x32x72 + } + else + rhc.hull = &model->brushq1.hulls[2]; // 64x64x64 + } + else + { + if (boxsize[0] <= 32) + rhc.hull = &model->brushq1.hulls[1]; // 32x32x56 + else + rhc.hull = &model->brushq1.hulls[2]; // 64x64x88 + } + VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start); + VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end); + VectorSubtract(rhc.end, rhc.start, rhc.dist); + Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end); } +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) +{ + int side, distz = endz - startz; + float front, back; + float mid; + +loc0: + if (node->contents < 0) + return false; // didn't hit anything + + switch (node->plane->type) + { + case PLANE_X: + node = node->children[x < node->plane->dist]; + goto loc0; + case PLANE_Y: + node = node->children[y < node->plane->dist]; + goto loc0; + case PLANE_Z: + side = startz < node->plane->dist; + if ((endz < node->plane->dist) == side) + { + node = node->children[side]; + goto loc0; + } + // found an intersection + mid = node->plane->dist; + break; + default: + back = front = x * node->plane->normal[0] + y * node->plane->normal[1]; + front += startz * node->plane->normal[2]; + back += endz * node->plane->normal[2]; + side = front < node->plane->dist; + if ((back < node->plane->dist) == side) + { + node = node->children[side]; + goto loc0; + } + // found an intersection + mid = startz + distz * (front - node->plane->dist) / (front - back); + break; + } + + // go down front side + if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid)) + return true; // hit something + else + { + // check for impact on this node + if (node->numsurfaces) + { + int i, ds, dt; + msurface_t *surf; + + surf = cl.worldmodel->brushq1.surfaces + node->firstsurface; + for (i = 0;i < node->numsurfaces;i++, surf++) + { + if (!(surf->flags & SURF_LIGHTMAP)) + continue; // no lightmaps + + 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]); + 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]); + + if (ds < surf->texturemins[0] || dt < surf->texturemins[1]) + continue; + + ds -= surf->texturemins[0]; + dt -= surf->texturemins[1]; + + if (ds > surf->extents[0] || dt > surf->extents[1]) + continue; + + if (surf->samples) + { + qbyte *lightmap; + 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; + line3 = ((surf->extents[0]>>4)+1)*3; + size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting + + lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color + + for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale; + r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale; + r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale; + r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale; + lightmap += size3; + } + /* -================= -Mod_LoadTextures -================= +LordHavoc: here's the readable version of the interpolation +code, not quite as easy for the compiler to optimize... + +dsfrac is the X position in the lightmap pixel, * 16 +dtfrac is the Y position in the lightmap pixel, * 16 +r00 is top left corner, r01 is top right corner +r10 is bottom left corner, r11 is bottom right corner +g and b are the same layout. +r0 and r1 are the top and bottom intermediate results + +first we interpolate the top two points, to get the top +edge sample + + r0 = (((r01-r00) * dsfrac) >> 4) + r00; + g0 = (((g01-g00) * dsfrac) >> 4) + g00; + b0 = (((b01-b00) * dsfrac) >> 4) + b00; + +then we interpolate the bottom two points, to get the +bottom edge sample + + r1 = (((r11-r10) * dsfrac) >> 4) + r10; + g1 = (((g11-g10) * dsfrac) >> 4) + g10; + b1 = (((b11-b10) * dsfrac) >> 4) + b10; + +then we interpolate the top and bottom samples to get the +middle sample (the one which was requested) + + r = (((r1-r0) * dtfrac) >> 4) + r0; + g = (((g1-g0) * dtfrac) >> 4) + g0; + b = (((b1-b0) * dtfrac) >> 4) + b0; */ -static void Mod_LoadTextures (lump_t *l) + + ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f); + ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f); + ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f); + } + return true; // success + } + } + + // go down back side + node = node->children[side ^ 1]; + startz = mid; + distz = endz - startz; + goto loc0; + } +} + +void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal) +{ + 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); +} + +static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend) +{ + int c; + while (out < outend) + { + if (in == inend) + { + Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n"); + return; + } + c = *in++; + if (c) + *out++ = c; + else + { + for (c = *in++;c > 0;c--) + { + if (out == outend) + { + Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n"); + return; + } + *out++ = 0; + } + } + } +} + +static void Mod_Q1BSP_LoadTextures(lump_t *l) { int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete; miptex_t *dmiptex; texture_t *tx, *tx2, *anims[10], *altanims[10]; dmiptexlump_t *m; - qbyte *data, *mtdata, *data2; + qbyte *data, *mtdata; char name[256]; - loadmodel->textures = NULL; - - if (!l->filelen) - return; - - m = (dmiptexlump_t *)(mod_base + l->fileofs); - - m->nummiptex = LittleLong (m->nummiptex); + loadmodel->brushq1.textures = NULL; // add two slots for notexture walls and notexture liquids - loadmodel->numtextures = m->nummiptex + 2; - loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(texture_t)); + if (l->filelen) + { + m = (dmiptexlump_t *)(mod_base + l->fileofs); + m->nummiptex = LittleLong (m->nummiptex); + loadmodel->brushq1.numtextures = m->nummiptex + 2; + } + else + { + m = NULL; + loadmodel->brushq1.numtextures = 2; + } + + loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t)); // fill out all slots with notexture - for (i = 0, tx = loadmodel->textures;i < loadmodel->numtextures;i++, tx++) + for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++) { + tx->number = i; + strcpy(tx->name, "NO TEXTURE FOUND"); tx->width = 16; tx->height = 16; - tx->texture = r_notexture; + tx->skin.base = r_notexture; tx->shader = &Cshader_wall_lightmap; - if (i == loadmodel->numtextures - 1) + tx->flags = SURF_SOLIDCLIP; + if (i == loadmodel->brushq1.numtextures - 1) { - tx->flags = SURF_DRAWTURB | SURF_LIGHTBOTHSIDES; + tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES; tx->shader = &Cshader_water; } + tx->currentframe = tx; } + if (!m) + return; + // just to work around bounds checking when debugging with it (array index out of bounds error thing) dofs = m->dataofs; // LordHavoc: mostly rewritten map texture loader @@ -256,30 +784,30 @@ static void Mod_LoadTextures (lump_t *l) name[j] = dmiptex->name[j]; name[j] = 0; - mtwidth = LittleLong (dmiptex->width); - mtheight = LittleLong (dmiptex->height); + mtwidth = LittleLong(dmiptex->width); + mtheight = LittleLong(dmiptex->height); mtdata = NULL; - j = LittleLong (dmiptex->offsets[0]); + j = LittleLong(dmiptex->offsets[0]); if (j) { // texture included if (j < 40 || j + mtwidth * mtheight > l->filelen) { - Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name); + Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name); continue; } mtdata = (qbyte *)dmiptex + j; } if ((mtwidth & 15) || (mtheight & 15)) - Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name); + Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name); // LordHavoc: force all names to lowercase for (j = 0;name[j];j++) if (name[j] >= 'A' && name[j] <= 'Z') name[j] += 'a' - 'A'; - tx = loadmodel->textures + i; + tx = loadmodel->brushq1.textures + i; strcpy(tx->name, name); tx->width = mtwidth; tx->height = mtheight; @@ -291,7 +819,7 @@ static void Mod_LoadTextures (lump_t *l) } // LordHavoc: HL sky textures are entirely different than quake - if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128) + if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128) { if (loadmodel->isworldmodel) { @@ -300,122 +828,81 @@ static void Mod_LoadTextures (lump_t *l) { if (image_width == 256 && image_height == 128) { - R_InitSky (data, 4); + R_InitSky(data, 4); Mem_Free(data); } else { Mem_Free(data); - Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name); + Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name); if (mtdata != NULL) - R_InitSky (mtdata, 1); + R_InitSky(mtdata, 1); } } else if (mtdata != NULL) - R_InitSky (mtdata, 1); + R_InitSky(mtdata, 1); } } - else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true))) - { - tx->fogtexture = image_masktex; - strcpy(name, tx->name); - strcat(name, "_glow"); - tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true); - } else { - if (loadmodel->ishlbsp) + if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true)) { - if (mtdata && (data = W_ConvertWAD3Texture(dmiptex))) - { - // texture included - tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE); - if (R_TextureHasAlpha(tx->texture)) - { - // make mask texture - for (j = 0;j < image_width * image_height;j++) - data[j*4+0] = data[j*4+1] = data[j*4+2] = 255; - strcpy(name, tx->name); - strcat(name, "_fog"); - tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE); - } - Mem_Free(data); - } - else if ((data = W_GetTexture(tx->name))) + // did not find external texture, load it from the bsp or wad3 + if (loadmodel->brush.ishlbsp) { - // get the size from the wad texture - tx->width = image_width; - tx->height = image_height; - tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE); - if (R_TextureHasAlpha(tx->texture)) + // internal texture overrides wad + qbyte *pixels, *freepixels, *fogpixels; + pixels = freepixels = NULL; + if (mtdata) + pixels = W_ConvertWAD3Texture(dmiptex); + if (pixels == NULL) + pixels = freepixels = W_GetTexture(tx->name); + if (pixels != NULL) { - // make mask texture - for (j = 0;j < image_width * image_height;j++) - data[j*4+0] = data[j*4+1] = data[j*4+2] = 255; - strcpy(name, tx->name); - strcat(name, "_fog"); - tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE); + tx->width = image_width; + tx->height = image_height; + 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); + if (Image_CheckAlpha(pixels, image_width * image_height, true)) + { + fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4); + for (j = 0;j < image_width * image_height * 4;j += 4) + { + fogpixels[j + 0] = 255; + fogpixels[j + 1] = 255; + fogpixels[j + 2] = 255; + fogpixels[j + 3] = pixels[j + 3]; + } + tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL); + Mem_Free(fogpixels); + } } - Mem_Free(data); - } - else - { - tx->width = 16; - tx->height = 16; - tx->texture = r_notexture; - } - } - else - { - if (mtdata) // texture included - { - int fullbrights; - data = mtdata; - fullbrights = false; - if (r_fullbrights.value && tx->name[0] != '*') - { - for (j = 0;j < tx->width*tx->height;j++) - { - if (data[j] >= 224) // fullbright - { - fullbrights = true; - break; - } - } - } - if (fullbrights) - { - data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height); - for (j = 0;j < tx->width*tx->height;j++) - data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights - tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE); - strcpy(name, tx->name); - strcat(name, "_glow"); - for (j = 0;j < tx->width*tx->height;j++) - data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights - tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE); - Mem_Free(data2); - } - else - tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE); - } - else // no texture, and no external replacement texture was found - { - tx->width = 16; - tx->height = 16; - tx->texture = r_notexture; + if (freepixels) + Mem_Free(freepixels); } + else if (mtdata) // texture included + Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height); } } + if (tx->skin.base == NULL) + { + // no texture found + tx->width = 16; + tx->height = 16; + tx->skin.base = r_notexture; + } if (tx->name[0] == '*') { + // turb does not block movement + tx->flags &= ~SURF_SOLIDCLIP; tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES; // LordHavoc: some turbulent textures should be fullbright and solid if (!strncmp(tx->name,"*lava",5) || !strncmp(tx->name,"*teleport",9) || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA; + else + tx->flags |= SURF_WATERALPHA; tx->shader = &Cshader_water; } else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y') @@ -426,31 +913,32 @@ static void Mod_LoadTextures (lump_t *l) else { tx->flags |= SURF_LIGHTMAP; - if (!tx->fogtexture) - tx->flags |= SURF_CLIPSOLID; + if (!tx->skin.fog) + tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT; tx->shader = &Cshader_wall_lightmap; } - tx->detailtexture = detailtextures[i % NUM_DETAILTEXTURES]; + // start out with no animation + tx->currentframe = tx; } // sequence the animations for (i = 0;i < m->nummiptex;i++) { - tx = loadmodel->textures + i; + tx = loadmodel->brushq1.textures + i; if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0) continue; if (tx->anim_total[0] || tx->anim_total[1]) continue; // already sequenced // find the number of frames in the animation - memset (anims, 0, sizeof(anims)); - memset (altanims, 0, sizeof(altanims)); + memset(anims, 0, sizeof(anims)); + memset(altanims, 0, sizeof(altanims)); for (j = i;j < m->nummiptex;j++) { - tx2 = loadmodel->textures + j; - if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2)) + tx2 = loadmodel->brushq1.textures + j; + if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2)) continue; num = tx2->name[1]; @@ -459,7 +947,7 @@ static void Mod_LoadTextures (lump_t *l) else if (num >= 'a' && num <= 'j') altanims[num - 'a'] = tx2; else - Con_Printf ("Bad animating texture %s\n", tx->name); + Con_Printf("Bad animating texture %s\n", tx->name); } max = altmax = 0; @@ -477,7 +965,7 @@ static void Mod_LoadTextures (lump_t *l) { if (!anims[j]) { - Con_Printf ("Missing frame %i of %s\n", j, tx->name); + Con_Printf("Missing frame %i of %s\n", j, tx->name); incomplete = true; } } @@ -485,7 +973,7 @@ static void Mod_LoadTextures (lump_t *l) { if (!altanims[j]) { - Con_Printf ("Missing altframe %i of %s\n", j, tx->name); + Con_Printf("Missing altframe %i of %s\n", j, tx->name); incomplete = true; } } @@ -536,39 +1024,34 @@ static void Mod_LoadTextures (lump_t *l) } } -/* -================= -Mod_LoadLighting -================= -*/ -static void Mod_LoadLighting (lump_t *l) +static void Mod_Q1BSP_LoadLighting(lump_t *l) { int i; qbyte *in, *out, *data, d; char litfilename[1024]; - loadmodel->lightdata = NULL; - if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight + loadmodel->brushq1.lightdata = NULL; + if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight { - loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen); - memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); + loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen); + memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen); } else // LordHavoc: bsp version 29 (normal white lighting) { // LordHavoc: hope is not lost yet, check for a .lit file to load strcpy(litfilename, loadmodel->name); - COM_StripExtension(litfilename, litfilename); + FS_StripExtension(litfilename, litfilename); strcat(litfilename, ".lit"); - data = (qbyte*) COM_LoadFile (litfilename, false); + data = (qbyte*) FS_LoadFile(litfilename, false); if (data) { - if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T') + if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T') { i = LittleLong(((int *)data)[1]); if (i == 1) { - Con_DPrintf("%s loaded", litfilename); - loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8); - memcpy(loadmodel->lightdata, data + 8, loadsize - 8); + Con_DPrintf("loaded %s\n", litfilename); + loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8); + memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8); Mem_Free(data); return; } @@ -580,7 +1063,7 @@ static void Mod_LoadLighting (lump_t *l) } else { - if (loadsize == 8) + if (fs_filesize == 8) Con_Printf("Empty .lit file, ignoring\n"); else Con_Printf("Corrupt .lit file (old version?), ignoring\n"); @@ -590,10 +1073,10 @@ static void Mod_LoadLighting (lump_t *l) // LordHavoc: oh well, expand the white lighting data if (!l->filelen) return; - loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3); - in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write - out = loadmodel->lightdata; - memcpy (in, mod_base + l->fileofs, l->filelen); + loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3); + in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write + out = loadmodel->brushq1.lightdata; + memcpy(in, mod_base + l->fileofs, l->filelen); for (i = 0;i < l->filelen;i++) { d = *in++; @@ -604,16 +1087,16 @@ static void Mod_LoadLighting (lump_t *l) } } -void Mod_LoadLightList(void) +static void Mod_Q1BSP_LoadLightList(void) { int a, n, numlights; char lightsfilename[1024], *s, *t, *lightsstring; mlight_t *e; strcpy(lightsfilename, loadmodel->name); - COM_StripExtension(lightsfilename, lightsfilename); + FS_StripExtension(lightsfilename, lightsfilename); strcat(lightsfilename, ".lights"); - s = lightsstring = (char *) COM_LoadFile (lightsfilename, false); + s = lightsstring = (char *) FS_LoadFile(lightsfilename, false); if (s) { numlights = 0; @@ -629,7 +1112,7 @@ void Mod_LoadLightList(void) s++; numlights++; } - loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t)); + loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t)); s = lightsstring; n = 0; while (*s && n < numlights) @@ -642,7 +1125,7 @@ void Mod_LoadLightList(void) Mem_Free(lightsstring); Host_Error("misparsed lights file!\n"); } - e = loadmodel->lights + n; + e = loadmodel->brushq1.lights + n; *s = 0; 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); *s = '\n'; @@ -659,2846 +1142,3423 @@ void Mod_LoadLightList(void) Mem_Free(lightsstring); Host_Error("misparsed lights file!\n"); } - loadmodel->numlights = numlights; + loadmodel->brushq1.numlights = numlights; Mem_Free(lightsstring); } } - - -/* -// svbspmesh_t is in model_brush.h - -typedef struct svbsppolygon_s -{ - struct svbsppolygon_s *next; - int numverts; - float *verts; - float normal[3], dist; -} -svbsppolygon_t; - -typedef struct svbspnode_s -{ - // true if this is a leaf (has no children), not a node - int isleaf; - // (shared) parent node - struct svbspnode_s *parent; - // (leaf) dark or lit leaf - int dark; - // (leaf) polygons bounding this leaf - svbsppolygon_t *polygons; - // (node) children - struct svbspnode_s *children[2]; - // (node) splitting plane - float normal[3], dist; -} -svbspnode_t; - -svbspnode_t *Mod_SVBSP_AllocNode(svbspnode_t *parent, svbspnode_t *child0, svbspnode_t *child1, float *normal, float dist) +static void Mod_Q1BSP_LoadVisibility(lump_t *l) { - svbspnode_t *node; - node = Mem_Alloc(loadmodel->mempool, sizeof(svbspnode_t)); - node->parent = parent; - node->children[0] = child0; - node->children[1] = child1; - VectorCopy(normal, node->normal); - node->dist = dist; - return node; -} - -svbspnode_t *Mod_SVBSP_AllocLeaf(svbspnode_t *parent, int dark) -{ - svbspnode_t *leaf; - leaf = Mem_Alloc(loadmodel->mempool, sizeof(svbspnode_t)); - leaf->isleaf = true; - leaf->parent = parent; - leaf->dark = dark; - return leaf; -} - -svbspnode_t *Mod_SVBSP_NewTree(void) -{ - return Mod_SVBSP_AllocLeaf(NULL, false); -} - -void Mod_SVBSP_FreeTree(svbspnode_t *node) -{ - if (!node->isleaf) - { - Mod_SVBSP_FreeTree(node->children[0]); - Mod_SVBSP_FreeTree(node->children[1]); - } - Mem_Free(node); + loadmodel->brushq1.num_compressedpvs = 0; + loadmodel->brushq1.data_compressedpvs = NULL; + if (!l->filelen) + return; + loadmodel->brushq1.num_compressedpvs = l->filelen; + loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen); + memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen); } -void Mod_SVBSP_RecursiveAddPolygon(svbspnode_t *node, int numverts, float *verts, float *normal, float dist, int constructmode) +// used only for HalfLife maps +static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data) { - int i, j, numvertsfront, numvertsback, maxverts, counts[3]; - float *vertsfront, *vertsback, *v, d, temp[3]; - float dists[4096]; - qbyte sides[4096]; - svbsppolygon_t *poly; - if (node->isleaf) + char key[128], value[4096]; + char wadname[128]; + int i, j, k; + if (!data) + return; + if (!COM_ParseToken(&data, false)) + return; // error + if (com_token[0] != '{') + return; // error + while (1) { - if (constructmode == 0) - { - // construct tree structure - node->isleaf = false; - node->children[0] = Mod_SVBSP_AllocLeaf(node, false); - node->children[1] = Mod_SVBSP_AllocLeaf(node, false); - VectorCopy(normal, node->normal); - node->dist = dist; - } - else if (constructmode == 1) - { - // mark dark leafs - node->dark = true; - } + if (!COM_ParseToken(&data, false)) + return; // error + if (com_token[0] == '}') + break; // end of worldspawn + if (com_token[0] == '_') + strcpy(key, com_token + 1); else + strcpy(key, com_token); + while (key[strlen(key)-1] == ' ') // remove trailing spaces + key[strlen(key)-1] = 0; + if (!COM_ParseToken(&data, false)) + return; // error + strcpy(value, com_token); + if (!strcmp("wad", key)) // for HalfLife maps { - // link polygons into lit leafs only (this is the optimization) - if (!node->dark) - { - poly = Mem_Alloc(loadmodel->mempool, sizeof(svbsppolygon_t) + numverts * sizeof(float[3])); - poly->numverts = numverts; - poly->verts = (float *)(poly + 1); - VectorCopy(normal, poly->normal); - poly->dist = dist; - memcpy(poly->verts, verts, numverts * sizeof(float[3])); - poly->next = node->polygons; - node->polygons = poly; - } - } - } - else - { - counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; - for (i = 0, v = verts;i < numverts;i++, v += 3) - { - dists[i] = DotProduct(v, node->normal) - node->dist; - if (dists[i] >= 0.1) - sides[i] = SIDE_FRONT; - else if (dists[i] <= -0.1) - sides[i] = SIDE_BACK; - else - sides[i] = SIDE_ON; - counts[sides[i]]++; - } - if (counts[SIDE_FRONT] && counts[SIDE_BACK]) - { - // some front, some back... sliced - numvertsfront = 0; - numvertsback = 0; - // this is excessive, but nice for safety... - maxverts = numverts + 4; - vertsfront = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3])); - vertsback = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3])); - for (i = 0, j = numverts - 1;i < numverts;j = i, i++) + if (loadmodel->brush.ishlbsp) { - if (sides[j] == SIDE_FRONT) - { - VectorCopy(&verts[j * 3], &vertsfront[numvertsfront * 3]); - numvertsfront++; - if (sides[i] == SIDE_BACK) - { - d = dists[j] / (dists[j] - dists[i]); - VectorSubtract(&verts[i * 3], &verts[j * 3], temp); - VectorMA(&verts[j * 3], d, temp, temp); - VectorCopy(temp, &vertsfront[numvertsfront * 3]); - VectorCopy(temp, &vertsback[numvertsback * 3]); - numvertsfront++; - numvertsback++; - } - } - else if (sides[j] == SIDE_BACK) + j = 0; + for (i = 0;i < 4096;i++) + if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':') + break; + if (value[i]) { - VectorCopy(&verts[j * 3], &vertsback[numvertsback * 3]); - numvertsback++; - if (sides[i] == SIDE_FRONT) + for (;i < 4096;i++) { - d = dists[j] / (dists[j] - dists[i]); - VectorSubtract(&verts[i * 3], &verts[j * 3], temp); - VectorMA(&verts[j * 3], d, temp, temp); - VectorCopy(temp, &vertsfront[numvertsfront * 3]); - VectorCopy(temp, &vertsback[numvertsback * 3]); - numvertsfront++; - numvertsback++; + // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'... + if (value[i] == '\\' || value[i] == '/' || value[i] == ':') + j = i+1; + else if (value[i] == ';' || value[i] == 0) + { + k = value[i]; + value[i] = 0; + strcpy(wadname, "textures/"); + strcat(wadname, &value[j]); + W_LoadTextureWadFile(wadname, false); + j = i+1; + if (!k) + break; + } } } - else - { - VectorCopy(&verts[j * 3], &vertsfront[numvertsfront * 3]); - VectorCopy(&verts[j * 3], &vertsback[numvertsback * 3]); - numvertsfront++; - numvertsback++; - } - } - Mod_SVBSP_RecursiveAddPolygon(node->children[1], numvertsfront, vertsfront, normal, dist, constructmode); - Mod_SVBSP_RecursiveAddPolygon(node->children[0], numvertsback, vertsback, normal, dist, constructmode); - Mem_Free(vertsfront); - Mem_Free(vertsback); - } - else if (counts[SIDE_BACK]) - Mod_SVBSP_RecursiveAddPolygon(node->children[0], numverts, verts, normal, dist, constructmode); - else if (counts[SIDE_FRONT]) - Mod_SVBSP_RecursiveAddPolygon(node->children[1], numverts, verts, normal, dist, constructmode); - else - { - // mode 0 is constructing tree, don't make unnecessary splits - if (constructmode == 1) - { - // marking dark leafs - // send it down the side it is not facing - Mod_SVBSP_RecursiveAddPolygon(node->children[DotProduct(node->normal, normal) < 0], numverts, verts, normal, dist, constructmode); - } - else if (constructmode == 2) - { - // linking polygons into lit leafs only - // send it down the side it is facing - Mod_SVBSP_RecursiveAddPolygon(node->children[DotProduct(node->normal, normal) >= 0], numverts, verts, normal, dist, constructmode); } } } } -int svbsp_count_nodes; -int svbsp_count_leafs; -int svbsp_count_polygons; -int svbsp_count_darkleafs; -int svbsp_count_originalpolygons; -int svbsp_count_meshs; -int svbsp_count_triangles; -int svbsp_count_vertices; - -void Mod_SVBSP_AddPolygon(svbspnode_t *root, int numverts, float *verts, int constructmode, float *test, int linenumber) +static void Mod_Q1BSP_LoadEntities(lump_t *l) { - int i; - float normal[3], dist, dir0[3], dir1[3], *v0, *v1, *v2; - svbsp_count_originalpolygons++; - for (i = 0, v0 = verts + (numverts - 2) * 3, v1 = verts + (numverts - 1) * 3, v2 = verts;i < numverts;i++, v0 = v1, v1 = v2, v2 += 3) - { - VectorSubtract(v0, v1, dir0); - VectorSubtract(v2, v1, dir1); - CrossProduct(dir0, dir1, normal); - if (DotProduct(normal, normal) >= 0.1) - break; - } - if (i == numverts) + loadmodel->brush.entities = NULL; + if (!l->filelen) return; - VectorNormalize(normal); - dist = DotProduct(verts, normal); - if (test && DotProduct(test, normal) > dist + 0.1) - Con_Printf("%i %f %f %f %f : %f %f %f %f\n", linenumber, normal[0], normal[1], normal[2], dist, test[0], test[1], test[2], DotProduct(test, normal)); - Mod_SVBSP_RecursiveAddPolygon(root, numverts, verts, normal, dist, constructmode); + loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen); + memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen); + if (loadmodel->brush.ishlbsp) + Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities); } -void Mod_SVBSP_RecursiveGatherStats(svbspnode_t *node) -{ - svbsppolygon_t *poly; - for (poly = node->polygons;poly;poly = poly->next) - svbsp_count_polygons++; - if (node->isleaf) - { - svbsp_count_leafs++; - if (node->dark) - svbsp_count_darkleafs++; - } - else - { - svbsp_count_nodes++; - Mod_SVBSP_RecursiveGatherStats(node->children[0]); - Mod_SVBSP_RecursiveGatherStats(node->children[1]); - } -} -svbspmesh_t *Mod_SVBSP_AllocMesh(int maxverts) +static void Mod_Q1BSP_LoadVertexes(lump_t *l) { - svbspmesh_t *mesh; - mesh = Mem_Alloc(loadmodel->mempool, sizeof(svbspmesh_t) + maxverts * sizeof(float[4]) + maxverts * sizeof(int[3])); - mesh->maxverts = maxverts; - mesh->maxtriangles = maxverts; - mesh->numverts = 0; - mesh->numtriangles = 0; - mesh->verts = (float *)(mesh + 1); - mesh->elements = (int *)(mesh->verts + mesh->maxverts * 4); - return mesh; -} + dvertex_t *in; + mvertex_t *out; + int i, count; -svbspmesh_t *Mod_SVBSP_ReAllocMesh(svbspmesh_t *oldmesh) -{ - svbspmesh_t *newmesh; - newmesh = Mem_Alloc(loadmodel->mempool, sizeof(svbspmesh_t) + oldmesh->numverts * sizeof(float[4]) + oldmesh->numtriangles * sizeof(int[3])); - newmesh->maxverts = newmesh->numverts = oldmesh->numverts; - newmesh->maxtriangles = newmesh->numtriangles = oldmesh->numtriangles; - newmesh->verts = (float *)(newmesh + 1); - newmesh->elements = (int *)(newmesh->verts + newmesh->maxverts * 4); - memcpy(newmesh->verts, oldmesh->verts, newmesh->numverts * sizeof(float[4])); - memcpy(newmesh->elements, oldmesh->elements, newmesh->numtriangles * sizeof(int[3])); - return newmesh; -} + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); -void Mod_SVBSP_RecursiveBuildTriangleMeshs(svbspmesh_t *firstmesh, svbspnode_t *node) -{ - svbsppolygon_t *poly; - svbspmesh_t *mesh; - int i, j, k; - float *v, *m, temp[3]; - if (node->isleaf) - { - for (poly = node->polygons;poly;poly = poly->next) - { - mesh = firstmesh; - while (poly->numverts + mesh->numverts > mesh->maxverts || (poly->numverts - 2) + mesh->numtriangles > mesh->maxtriangles) - { - if (mesh->next == NULL) - mesh->next = Mod_SVBSP_AllocMesh(max(1000, poly->numverts)); - mesh = mesh->next; - } - for (i = 0, v = poly->verts;i < poly->numverts - 2;i++, v += 3) - { - for (k = 0;k < 3;k++) - { - if (k == 0) - v = poly->verts; - else if (k == 1) - v = poly->verts + (i + 1) * 3; - else if (k == 2) - v = poly->verts + (i + 2) * 3; - for (j = 0, m = mesh->verts;j < mesh->numverts;j++, m += 4) - { - VectorSubtract(v, m, temp); - if (DotProduct(temp, temp) < 0.1) - break; - } - if (j == mesh->numverts) - { - mesh->numverts++; - VectorCopy(v, m); - } - mesh->elements[mesh->numtriangles * 3 + k] = j; - } - mesh->numtriangles++; - } - } - } - else + loadmodel->brushq1.vertexes = out; + loadmodel->brushq1.numvertexes = count; + + for ( i=0 ; ichildren[0]); - Mod_SVBSP_RecursiveBuildTriangleMeshs(firstmesh, node->children[1]); + out->position[0] = LittleFloat(in->point[0]); + out->position[1] = LittleFloat(in->point[1]); + out->position[2] = LittleFloat(in->point[2]); } } -svbspmesh_t *Mod_SVBSP_BuildTriangleMeshs(svbspnode_t *root, vec3_t mins, vec3_t maxs) +static void Mod_Q1BSP_LoadSubmodels(lump_t *l) { - svbspmesh_t *firstmesh, *mesh, *newmesh, *nextmesh; - int i; - float *v; - firstmesh = Mod_SVBSP_AllocMesh(1000); - Mod_SVBSP_RecursiveBuildTriangleMeshs(firstmesh, root); - // reallocate meshs to conserve space - for (mesh = firstmesh, firstmesh = NULL;mesh;mesh = nextmesh) - { - svbsp_count_meshs++; - svbsp_count_triangles += mesh->numtriangles; - svbsp_count_vertices += mesh->numverts; + dmodel_t *in; + dmodel_t *out; + int i, j, count; - // calculate bbox - if (firstmesh == NULL) - { - VectorCopy(mesh->verts, mins); - VectorCopy(mesh->verts, maxs); - } - for (i = 0, v = mesh->verts;i < mesh->numverts;i++, v += 4) - { - if (mins[0] > v[0]) mins[0] = v[0];if (maxs[0] < v[0]) maxs[0] = v[0]; - if (mins[1] > v[1]) mins[1] = v[1];if (maxs[1] < v[1]) maxs[1] = v[1]; - if (mins[2] > v[2]) mins[2] = v[2];if (maxs[2] < v[2]) maxs[2] = v[2]; - } + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); - nextmesh = mesh->next; - newmesh = Mod_SVBSP_ReAllocMesh(mesh); - newmesh->next = firstmesh; - firstmesh = newmesh; - Mem_Free(mesh); - } - return firstmesh; -} + loadmodel->brushq1.submodels = out; + loadmodel->brush.numsubmodels = count; -void Mod_SVBSP_FreeTriangleMeshs(svbspmesh_t *mesh) -{ - svbspmesh_t *nextmesh; - for (;mesh;mesh = nextmesh) + for ( i=0 ; inext; - Mem_Free(mesh); + for (j=0 ; j<3 ; j++) + { + // spread the mins / maxs by a pixel + out->mins[j] = LittleFloat(in->mins[j]) - 1; + out->maxs[j] = LittleFloat(in->maxs[j]) + 1; + out->origin[j] = LittleFloat(in->origin[j]); + } + for (j=0 ; jheadnode[j] = LittleLong(in->headnode[j]); + out->visleafs = LittleLong(in->visleafs); + out->firstface = LittleLong(in->firstface); + out->numfaces = LittleLong(in->numfaces); } } -*/ - -typedef struct svpolygon_s -{ - struct svpolygon_s *next; - int maxverts; - int numverts; - float *verts; - float normal[3], dist; -} -svpolygon_t; -typedef struct svbrush_s +static void Mod_Q1BSP_LoadEdges(lump_t *l) { - struct svbrush_s *next; - svpolygon_t *polygons; - vec3_t mins, maxs; -} -svbrush_t; + dedge_t *in; + medge_t *out; + int i, count; -typedef struct svworld_s -{ - svbrush_t *brushs; -} -svworld_t; + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); -svworld_t *Mod_ShadowBrush_NewWorld(mempool_t *mempool) -{ - return Mem_Alloc(mempool, sizeof(svworld_t)); -} + loadmodel->brushq1.edges = out; + loadmodel->brushq1.numedges = count; -void Mod_ShadowBrush_FreeWorld(svworld_t *world) -{ - svbrush_t *brush, *brushnext; - svpolygon_t *poly, *polynext; - for (brush = world->brushs;brush;brush = brushnext) + for ( i=0 ; inext; - for (poly = brush->polygons;poly;poly = polynext) - { - polynext = poly->next; - Mem_Free(poly); - } - Mem_Free(brush); + out->v[0] = (unsigned short)LittleShort(in->v[0]); + out->v[1] = (unsigned short)LittleShort(in->v[1]); } - Mem_Free(world); } -svbrush_t *Mod_ShadowBrush_BeginBrush(mempool_t *mempool) +static void Mod_Q1BSP_LoadTexinfo(lump_t *l) { - return Mem_Alloc(mempool, sizeof(svbrush_t)); -} + texinfo_t *in; + mtexinfo_t *out; + int i, j, k, count, miptex; -void Mod_ShadowBrush_AddPolygon(mempool_t *mempool, svbrush_t *brush, int numverts, float *verts) -{ - int i; - float normal[3], dist, dir0[3], dir1[3], *v0, *v1, *v2; - svpolygon_t *poly; - for (i = 0, v0 = verts + (numverts - 2) * 3, v1 = verts + (numverts - 1) * 3, v2 = verts;i < numverts;i++, v0 = v1, v1 = v2, v2 += 3) - { - VectorSubtract(v0, v1, dir0); - VectorSubtract(v2, v1, dir1); - CrossProduct(dir0, dir1, normal); - if (DotProduct(normal, normal) >= 0.1) - break; - } - if (i == numverts) - return; - VectorNormalize(normal); - dist = DotProduct(verts, normal); + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - poly = Mem_Alloc(mempool, sizeof(svpolygon_t) + numverts * sizeof(float[3])); - poly->numverts = numverts; - poly->verts = (float *)(poly + 1); - VectorCopy(normal, poly->normal); - poly->dist = dist; - poly->next = brush->polygons; - brush->polygons = poly; - memcpy(poly->verts, verts, numverts * sizeof(float[3])); -} + loadmodel->brushq1.texinfo = out; + loadmodel->brushq1.numtexinfo = count; -void Mod_ShadowBrush_EndBrush(svworld_t *world, svbrush_t *brush) -{ - int i; - float *v; - svpolygon_t *poly; - if (!brush->polygons) - { - Mem_Free(brush); - return; - } - brush->next = world->brushs; - world->brushs = brush; - VectorCopy(brush->polygons->verts, brush->mins); - VectorCopy(brush->polygons->verts, brush->maxs); - for (poly = brush->polygons;poly;poly = poly->next) + for (i = 0;i < count;i++, in++, out++) { - for (i = 0, v = poly->verts;i < poly->numverts;i++, v += 3) + for (k = 0;k < 2;k++) + for (j = 0;j < 4;j++) + out->vecs[k][j] = LittleFloat(in->vecs[k][j]); + + miptex = LittleLong(in->miptex); + out->flags = LittleLong(in->flags); + + out->texture = NULL; + if (loadmodel->brushq1.textures) { - if (brush->mins[0] > v[0]) brush->mins[0] = v[0];if (brush->maxs[0] < v[0]) brush->maxs[0] = v[0]; - if (brush->mins[1] > v[1]) brush->mins[1] = v[1];if (brush->maxs[1] < v[1]) brush->maxs[1] = v[1]; - if (brush->mins[2] > v[2]) brush->mins[2] = v[2];if (brush->maxs[2] < v[2]) brush->maxs[2] = v[2]; + if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures) + Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures); + else + out->texture = loadmodel->brushq1.textures + miptex; + } + if (out->flags & TEX_SPECIAL) + { + // if texture chosen is NULL or the shader needs a lightmap, + // force to notexture water shader + if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP) + out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1); + } + else + { + // if texture chosen is NULL, force to notexture + if (out->texture == NULL) + out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2); } } } -void Mod_ShadowBrush_ProcessWorld(mempool_t *mempool, svworld_t *world) +#if 0 +void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs) { - /* - for (clipbrush = world->brushs;clipbrush;clipbrush = clipbrush->next) + int i, j; + float *v; + + mins[0] = mins[1] = mins[2] = 9999; + maxs[0] = maxs[1] = maxs[2] = -9999; + v = verts; + for (i = 0;i < numverts;i++) { - for (brush = world->brushs;brush;brush = brush->next) + for (j = 0;j < 3;j++, v++) { - if (brush != clipbrush - && brush->mins[0] <= clipbrush->maxs[0] - && brush->maxs[0] >= clipbrush->mins[0] - && brush->mins[1] <= clipbrush->maxs[1] - && brush->maxs[1] >= clipbrush->mins[1] - && brush->mins[2] <= clipbrush->maxs[2] - && brush->maxs[2] >= clipbrush->mins[2]) - continue; - for (poly = brush->polygons;poly;poly = poly->next) - { - - } + if (*v < mins[j]) + mins[j] = *v; + if (*v > maxs[j]) + maxs[j] = *v; } } - */ } -shadowmesh_t *Mod_ShadowBrush_BuildMeshs(mempool_t *mempool, svworld_t *world) -{ - shadowmesh_t *mesh; - svbrush_t *brush; - svpolygon_t *poly; - mesh = Mod_ShadowMesh_Begin(mempool); - for (brush = world->brushs;brush;brush = brush->next) - for (poly = brush->polygons;poly;poly = poly->next) - Mod_ShadowMesh_AddPolygon(mempool, mesh, poly->numverts, poly->verts); - mesh = Mod_ShadowMesh_Finish(mempool, mesh); - return mesh; -} +#define MAX_SUBDIVPOLYTRIANGLES 4096 +#define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3) + +static int subdivpolyverts, subdivpolytriangles; +static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3]; +static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3]; -void Mod_ProcessLightList(void) +static int subdivpolylookupvert(vec3_t v) { - int j, k, *mark, lnum; - mlight_t *e; - msurface_t *surf; - float dist; - mleaf_t *leaf; - qbyte *pvs; - for (lnum = 0, e = loadmodel->lights;lnum < loadmodel->numlights;lnum++, e++) - { - e->cullradius2 = DotProduct(e->light, e->light) * (1.0f / (8192.0f * 8192.0f)) / (e->falloff * e->falloff) + 4096.0f; - if (e->cullradius2 > 4096.0f * 4096.0f) - e->cullradius2 = 4096.0f * 4096.0f; - e->cullradius = sqrt(e->cullradius2); - leaf = Mod_PointInLeaf(e->origin, loadmodel); - if (leaf->compressed_vis) - pvs = Mod_DecompressVis (leaf->compressed_vis, loadmodel); - else - pvs = mod_novis; - for (j = 0;j < loadmodel->numsurfaces;j++) - loadmodel->surfacevisframes[j] = -1; - for (j = 0, leaf = loadmodel->leafs + 1;j < loadmodel->numleafs - 1;j++, leaf++) + int i; + for (i = 0;i < subdivpolyverts;i++) + if (subdivpolyvert[i][0] == v[0] + && subdivpolyvert[i][1] == v[1] + && subdivpolyvert[i][2] == v[2]) + return i; + if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS) + Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size"); + VectorCopy(v, subdivpolyvert[subdivpolyverts]); + return subdivpolyverts++; +} + +static void SubdividePolygon(int numverts, float *verts) +{ + int i, i1, i2, i3, f, b, c, p; + vec3_t mins, maxs, front[256], back[256]; + float m, *pv, *cv, dist[256], frac; + + if (numverts > 250) + Host_Error("SubdividePolygon: ran out of verts in buffer"); + + BoundPoly(numverts, verts, mins, maxs); + + for (i = 0;i < 3;i++) + { + m = (mins[i] + maxs[i]) * 0.5; + m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5); + if (maxs[i] - m < 8) + continue; + if (m - mins[i] < 8) + continue; + + // cut it + for (cv = verts, c = 0;c < numverts;c++, cv += 3) + dist[c] = cv[i] - m; + + f = b = 0; + for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3) { - if (pvs[j >> 3] & (1 << (j & 7))) + if (dist[p] >= 0) { - for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++) - { - surf = loadmodel->surfaces + *mark; - if (surf->number != *mark) - Con_Printf("%d != %d\n", surf->number, *mark); - dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist; - if (surf->flags & SURF_PLANEBACK) - dist = -dist; - if (dist > 0 && dist < e->cullradius) - loadmodel->surfacevisframes[*mark] = -2; - } + VectorCopy(pv, front[f]); + f++; } - } - // build list of light receiving surfaces - e->numsurfaces = 0; - for (j = 0;j < loadmodel->numsurfaces;j++) - if (loadmodel->surfacevisframes[j] == -2) - e->numsurfaces++; - e->surfaces = NULL; - if (e->numsurfaces > 0) - { - e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces); - e->numsurfaces = 0; - for (j = 0;j < loadmodel->numsurfaces;j++) - if (loadmodel->surfacevisframes[j] == -2) - e->surfaces[e->numsurfaces++] = loadmodel->surfaces + j; - } - { - // find bounding box and sphere of lit surfaces - // (these will be used for creating a shape to clip the light) - float *v, temp[3], radius2; - radius2 = 0; - for (j = 0;j < e->numsurfaces;j++) - { - surf = e->surfaces[j]; - if (j == 0) + if (dist[p] <= 0) { - VectorCopy(surf->poly_verts, e->mins); - VectorCopy(surf->poly_verts, e->maxs); + VectorCopy(pv, back[b]); + b++; } - for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3) + if (dist[p] == 0 || dist[c] == 0) + continue; + if ((dist[p] > 0) != (dist[c] > 0) ) { - if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0]; - if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1]; - if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2]; - VectorSubtract(v, e->origin, temp); - dist = DotProduct(temp, temp); - if (radius2 < dist) - radius2 = dist; + // clip point + frac = dist[p] / (dist[p] - dist[c]); + front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]); + front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]); + front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]); + f++; + b++; } } - /* - if (e->cullradius2 > radius2) + + SubdividePolygon(f, front[0]); + SubdividePolygon(b, back[0]); + return; + } + + i1 = subdivpolylookupvert(verts); + i2 = subdivpolylookupvert(verts + 3); + for (i = 2;i < numverts;i++) + { + if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES) { - e->cullradius2 = radius2; - e->cullradius = sqrt(e->cullradius2); - } - */ - } -#if 1 - // clip shadow volumes against eachother to remove unnecessary - // polygons (and sections of polygons) - { - vec3_t temp, outermins, outermaxs, innermins, innermaxs; - int maxverts = 4; - float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3])); - float f, *v0, *v1, projectdistance; - svworld_t *svworld; - svbrush_t *svbrush; - - innermins[0] = e->mins[0] - 1; - innermins[1] = e->mins[1] - 1; - innermins[2] = e->mins[2] - 1; - innermaxs[0] = e->maxs[0] + 1; - innermaxs[1] = e->maxs[1] + 1; - innermaxs[2] = e->maxs[2] + 1; - outermins[0] = loadmodel->normalmins[0] - 1; - outermins[1] = loadmodel->normalmins[1] - 1; - outermins[2] = loadmodel->normalmins[2] - 1; - outermaxs[0] = loadmodel->normalmaxs[0] + 1; - outermaxs[1] = loadmodel->normalmaxs[1] + 1; - outermaxs[2] = loadmodel->normalmaxs[2] + 1; - svworld = Mod_ShadowBrush_NewWorld(loadmodel->mempool); - for (j = 0, surf = loadmodel->surfaces + loadmodel->firstmodelsurface;j < loadmodel->nummodelsurfaces;j++, surf++) - { - if (!(surf->flags & SURF_CLIPSOLID)) - continue; - f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist; - if (surf->flags & SURF_PLANEBACK) - f = -f; - projectdistance = e->cullradius + f; - if (projectdistance < 0.1 || projectdistance > e->cullradius) - continue; - VectorSubtract(e->origin, surf->poly_center, temp); - if (DotProduct(temp, temp) > (surf->poly_radius2 + e->cullradius2)) - continue; - if (maxverts < surf->poly_numverts) - { - maxverts = surf->poly_numverts; - if (verts) - Mem_Free(verts); - verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3])); - } - svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool); - // copy the original polygon, reversed, for the front cap of the volume - for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3) - VectorCopy(v0, v1); - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, surf->poly_numverts, verts); - // project the original polygon, for the back cap of the volume - for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3) - { - VectorSubtract(v0, e->origin, temp); - VectorNormalize(temp); - VectorMA(v0, projectdistance, temp, v1); - } - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, surf->poly_numverts, verts); - // project the shadow volume sides - for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;k++, v0 = v1, v1 += 3) - { - VectorCopy(v0, &verts[0]); - VectorCopy(v1, &verts[3]); - VectorCopy(v1, &verts[6]); - VectorCopy(v0, &verts[9]); - VectorSubtract(&verts[6], e->origin, temp); - VectorNormalize(temp); - VectorMA(&verts[6], projectdistance, temp, &verts[6]); - VectorSubtract(&verts[9], e->origin, temp); - VectorNormalize(temp); - VectorMA(&verts[9], projectdistance, temp, &verts[9]); - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - } - Mod_ShadowBrush_EndBrush(svworld, svbrush); - } - // add bounding box around the whole shadow volume set, - // facing inward to limit light area, with an outer bounding box - // facing outward (this is needed by the shadow rendering method) - // X major - svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool); - verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2]; - verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2]; - verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2]; - verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2]; - verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2]; - verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2]; - verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - Mod_ShadowBrush_EndBrush(svworld, svbrush); - // X minor - svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool); - verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2]; - verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2]; - verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2]; - verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2]; - verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2]; - verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2]; - verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - Mod_ShadowBrush_EndBrush(svworld, svbrush); - // Y major - svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool); - verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2]; - verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2]; - verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2]; - verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2]; - verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2]; - verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2]; - verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - Mod_ShadowBrush_EndBrush(svworld, svbrush); - // Y minor - svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool); - verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2]; - verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2]; - verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2]; - verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2]; - verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2]; - verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2]; - verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - Mod_ShadowBrush_EndBrush(svworld, svbrush); - // Z major - svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool); - verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2]; - verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2]; - verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2]; - verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2]; - verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2]; - verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2]; - verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - Mod_ShadowBrush_EndBrush(svworld, svbrush); - // Z minor - svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool); - verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2]; - verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2]; - verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2]; - verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2]; - verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2]; - verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2]; - verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2]; - Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts); - Mod_ShadowBrush_EndBrush(svworld, svbrush); - // clip away hidden polygons - Mod_ShadowBrush_ProcessWorld(loadmodel->mempool, svworld); - // build the triangle mesh - e->shadowvolume = Mod_ShadowBrush_BuildMeshs(loadmodel->mempool, svworld); - Mod_ShadowBrush_FreeWorld(svworld); - } -#elif 0 - // build svbsp (shadow volume bsp) - { - int maxverts = 0, constructmode; - float *verts = NULL, projectdistance, *v0, *v1, f, temp[3]; - svbspnode_t *svbsproot; - svbsproot = Mod_SVBSP_NewTree(); - // we do this in three stages: - // 1. construct the svbsp structure - // 2. mark which leafs are dark (shadow) - // 3. link polygons into only leafs that are not dark - // this results in polygons that are only on the outside of the - // shadow volume, removing polygons that are inside the shadow - // volume (which waste time) - for (constructmode = 0;constructmode < 3;constructmode++) - { - svbsp_count_originalpolygons = 0; -#if 1 - for (j = 0, surf = loadmodel->surfaces + loadmodel->firstmodelsurface;j < loadmodel->nummodelsurfaces;j++, surf++) - { - if (!(surf->flags & SURF_CLIPSOLID)) - continue; - /* - if (surf->poly_maxs[0] < e->mins[0] - || surf->poly_mins[0] > e->maxs[0] - || surf->poly_maxs[1] < e->mins[1] - || surf->poly_mins[1] > e->maxs[1] - || surf->poly_maxs[2] < e->mins[2] - || surf->poly_mins[2] > e->maxs[2]) - continue; - */ - f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist; - if (surf->flags & SURF_PLANEBACK) - f = -f; - projectdistance = e->cullradius + f; - if (projectdistance < 0.1 || projectdistance > e->cullradius) - continue; - /* - // find the nearest vertex of the projected volume - for (k = 0, v0 = surf->poly_verts;k < surf->poly_numverts;k++, v0 += 3) - { - VectorSubtract(v0, e->origin, temp); - VectorNormalize(temp); - if (maxdist00 > v0[0] - e->origin[0]) maxdist00 = v0[0] - e->origin[0]; - if (maxdist01 < e->origin[0] - v0[0]) maxdist01 = e->origin[0] - v0[0]; - if (maxdist10 > v0[1] - e->origin[1]) maxdist10 = v0[1] - e->origin[1]; - if (maxdist11 < e->origin[1] - v0[1]) maxdist11 = e->origin[1] - v0[1]; - if (maxdist20 > v0[2] - e->origin[2]) maxdist20 = v0[2] - e->origin[2]; - if (maxdist21 < e->origin[2] - v0[2]) maxdist21 = e->origin[2] - v0[2]; - dist = - - dist = DotProduct(temp, temp); - if (bestdist > dist) - { - bestdist = dist; - VectorCopy(temp, bestvec); - } - } - projectdistance = e->cullradius - sqrt(bestdist); - if (projectdistance < 0.1) - continue; - for (k = 0, v0 = surf->poly_verts;k < surf->poly_numverts;k++, v0 += 3) - { - VectorNormalize(temp); - if (temp[0] > 0) - { - dist = (e->maxs[0] - e->origin[0]) / temp[0]; - if (maxdist > - } - else if (temp[0] < 0) - dist = (e->mins[0] - e->origin[0]) / temp[0]; - dist = - VectorMA(v0, projectdistance, temp, temp); - dist = (temp[0] - VectorSubtract(temp, e->origin, - } - */ - VectorSubtract(e->origin, surf->poly_center, temp); - if (DotProduct(temp, temp) > (surf->poly_radius2 + e->cullradius2)) - continue; - if (maxverts < surf->poly_numverts) - { - maxverts = surf->poly_numverts; - if (verts) - Mem_Free(verts); - verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3])); - } - // copy the original polygon, reversed, for the front cap of the volume - for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3) - VectorCopy(v0, v1); - Mod_SVBSP_AddPolygon(svbsproot, surf->poly_numverts, verts, constructmode, surf->poly_center, __LINE__); - // project the original polygon, for the back cap of the volume - for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3) - { - VectorSubtract(v0, e->origin, temp); - VectorNormalize(temp); - VectorMA(v0, projectdistance, temp, v1); - } - Mod_SVBSP_AddPolygon(svbsproot, surf->poly_numverts, verts, constructmode, surf->poly_center, __LINE__); - // project the shadow volume sides - for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;k++, v0 = v1, v1 += 3) - { - VectorCopy(v0, &verts[0]); - VectorCopy(v1, &verts[3]); - VectorCopy(v1, &verts[6]); - VectorCopy(v0, &verts[9]); - VectorSubtract(&verts[6], e->origin, temp); - VectorNormalize(temp); - VectorMA(&verts[6], projectdistance, temp, &verts[6]); - VectorSubtract(&verts[9], e->origin, temp); - VectorNormalize(temp); - VectorMA(&verts[9], projectdistance, temp, &verts[9]); - Mod_SVBSP_AddPolygon(svbsproot, 4, verts, constructmode, surf->poly_center, __LINE__); - } - } -#else - for (j = 0;j < e->numsurfaces;j++) - { - surf = e->surfaces[j]; - if (!(surf->flags & SURF_CLIPSOLID)) - continue; - f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist; - if (surf->flags & SURF_PLANEBACK) - f = -f; - projectdistance = e->cullradius - f; - if (projectdistance < 0.1 || projectdistance > e->cullradius) - continue; - VectorSubtract(e->origin, surf->poly_center, temp); - if (DotProduct(temp, temp) > (surf->poly_radius2 + e->cullradius2)) - continue; - if (maxverts < surf->poly_numverts) - { - maxverts = surf->poly_numverts; - if (verts) - Mem_Free(verts); - verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3])); - } - // copy the original polygon, for the front cap of the volume - for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3) - VectorCopy(v0, v1); - Mod_SVBSP_AddPolygon(svbsproot, surf->poly_numverts, verts, constructmode, surf->poly_center, __LINE__); - // project the original polygon, reversed, for the back cap of the volume - for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3) - { - VectorSubtract(v0, e->origin, temp); - VectorNormalize(temp); - VectorMA(v0, projectdistance, temp, v1); - } - Mod_SVBSP_AddPolygon(svbsproot, surf->poly_numverts, verts, constructmode, surf->poly_center, __LINE__); - // project the shadow volume sides - for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;k++, v0 = v1, v1 += 3) - { - VectorCopy(v1, &verts[0]); - VectorCopy(v0, &verts[3]); - VectorCopy(v0, &verts[6]); - VectorCopy(v1, &verts[9]); - VectorSubtract(&verts[6], e->origin, temp); - VectorNormalize(temp); - VectorMA(&verts[6], projectdistance, temp, &verts[6]); - VectorSubtract(&verts[9], e->origin, temp); - VectorNormalize(temp); - VectorMA(&verts[9], projectdistance, temp, &verts[9]); - Mod_SVBSP_AddPolygon(svbsproot, 4, verts, constructmode, surf->poly_center, __LINE__); - } - } -#endif - } - if (verts) - Mem_Free(verts); - - svbsp_count_nodes = 0; - svbsp_count_leafs = 0; - svbsp_count_polygons = 0; - svbsp_count_darkleafs = 0; - svbsp_count_meshs = 0; - svbsp_count_triangles = 0; - svbsp_count_vertices = 0; - e->shadowvolume = Mod_SVBSP_BuildTriangleMeshs(svbsproot, e->shadowvolumemins, e->shadowvolumemaxs); - Mod_SVBSP_RecursiveGatherStats(svbsproot); - Mod_SVBSP_FreeTree(svbsproot); - Con_Printf("light %d (radius %d) has %d surfaces, svbsp contains %d nodes, %d leafs, %d are dark (%d%%), %d original polygons, %d polygons stored (%d%%), %d meshs %d vertices %d triangles\n", lnum, (int)e->cullradius, e->numsurfaces, svbsp_count_nodes, svbsp_count_leafs, svbsp_count_darkleafs, svbsp_count_leafs ? (100 * svbsp_count_darkleafs / svbsp_count_leafs) : 0, svbsp_count_originalpolygons, svbsp_count_polygons, svbsp_count_originalpolygons ? (100 * svbsp_count_polygons / svbsp_count_originalpolygons) : 0, svbsp_count_meshs, svbsp_count_triangles, svbsp_count_vertices); + Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n"); + return; } -#endif + + i3 = subdivpolylookupvert(verts + i * 3); + subdivpolyindex[subdivpolytriangles][0] = i1; + subdivpolyindex[subdivpolytriangles][1] = i2; + subdivpolyindex[subdivpolytriangles][2] = i3; + i2 = i3; + subdivpolytriangles++; } } - -/* -================= -Mod_LoadVisibility -================= -*/ -static void Mod_LoadVisibility (lump_t *l) +//Breaks a polygon up along axial 64 unit +//boundaries so that turbulent and sky warps +//can be done reasonably. +static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf) { - loadmodel->visdata = NULL; - if (!l->filelen) - return; - loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen); - memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); -} + int i, j; + surfvertex_t *v; + surfmesh_t *mesh; -// used only for HalfLife maps -void Mod_ParseWadsFromEntityLump(const char *data) -{ - char key[128], value[4096]; - char wadname[128]; - int i, j, k; - if (!data) - return; - if (!COM_ParseToken(&data)) - return; // error - if (com_token[0] != '{') - return; // error - while (1) + subdivpolytriangles = 0; + subdivpolyverts = 0; + SubdividePolygon(surf->poly_numverts, surf->poly_verts); + if (subdivpolytriangles < 1) + Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n"); + + surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t)); + mesh->num_vertices = subdivpolyverts; + mesh->num_triangles = subdivpolytriangles; + mesh->vertex = (surfvertex_t *)(mesh + 1); + mesh->index = (int *)(mesh->vertex + mesh->num_vertices); + memset(mesh->vertex, 0, mesh->num_vertices * sizeof(surfvertex_t)); + + for (i = 0;i < mesh->num_triangles;i++) + for (j = 0;j < 3;j++) + mesh->index[i*3+j] = subdivpolyindex[i][j]; + + for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++) { - if (!COM_ParseToken(&data)) - return; // error - if (com_token[0] == '}') - break; // end of worldspawn - if (com_token[0] == '_') - strcpy(key, com_token + 1); - else - strcpy(key, com_token); - while (key[strlen(key)-1] == ' ') // remove trailing spaces - key[strlen(key)-1] = 0; - if (!COM_ParseToken(&data)) - return; // error - strcpy(value, com_token); - if (!strcmp("wad", key)) // for HalfLife maps - { - if (loadmodel->ishlbsp) - { - j = 0; - for (i = 0;i < 4096;i++) - if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':') - break; - if (value[i]) - { - for (;i < 4096;i++) - { - // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'... - if (value[i] == '\\' || value[i] == '/' || value[i] == ':') - j = i+1; - else if (value[i] == ';' || value[i] == 0) - { - k = value[i]; - value[i] = 0; - strcpy(wadname, "textures/"); - strcat(wadname, &value[j]); - W_LoadTextureWadFile (wadname, false); - j = i+1; - if (!k) - break; - } - } - } - } - } + VectorCopy(subdivpolyvert[i], v->v); + v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]); + v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]); } } +#endif -/* -================= -Mod_LoadEntities -================= -*/ -static void Mod_LoadEntities (lump_t *l) +static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles) { - loadmodel->entities = NULL; - if (!l->filelen) - return; - loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen); - memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); - if (loadmodel->ishlbsp) - Mod_ParseWadsFromEntityLump(loadmodel->entities); + surfmesh_t *mesh; + mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float)); + mesh->num_vertices = numverts; + mesh->num_triangles = numtriangles; + mesh->data_vertex3f = (float *)(mesh + 1); + mesh->data_texcoordtexture2f = mesh->data_vertex3f + mesh->num_vertices * 3; + mesh->data_texcoordlightmap2f = mesh->data_texcoordtexture2f + mesh->num_vertices * 2; + mesh->data_texcoorddetail2f = mesh->data_texcoordlightmap2f + mesh->num_vertices * 2; + mesh->data_svector3f = (float *)(mesh->data_texcoorddetail2f + mesh->num_vertices * 2); + mesh->data_tvector3f = mesh->data_svector3f + mesh->num_vertices * 3; + mesh->data_normal3f = mesh->data_tvector3f + mesh->num_vertices * 3; + mesh->data_lightmapoffsets = (int *)(mesh->data_normal3f + mesh->num_vertices * 3); + mesh->data_element3i = mesh->data_lightmapoffsets + mesh->num_vertices; + mesh->data_neighbor3i = mesh->data_element3i + mesh->num_triangles * 3; + return mesh; } - -/* -================= -Mod_LoadVertexes -================= -*/ -static void Mod_LoadVertexes (lump_t *l) +static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges) { - dvertex_t *in; - mvertex_t *out; - int i, count; + int i, lindex, j; + float *vec, *vert, mins[3], maxs[3], val, *v; + mtexinfo_t *tex; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + // convert edges back to a normal polygon + surf->poly_numverts = numedges; + vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges); + for (i = 0;i < numedges;i++) + { + lindex = loadmodel->brushq1.surfedges[firstedge + i]; + if (lindex > 0) + vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position; + else + vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position; + VectorCopy(vec, vert); + vert += 3; + } - loadmodel->vertexes = out; - loadmodel->numvertexes = count; + // calculate polygon bounding box and center + vert = surf->poly_verts; + VectorCopy(vert, mins); + VectorCopy(vert, maxs); + vert += 3; + for (i = 1;i < surf->poly_numverts;i++, vert += 3) + { + if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0]; + if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1]; + if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2]; + } + VectorCopy(mins, surf->poly_mins); + VectorCopy(maxs, surf->poly_maxs); + surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f; + surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f; + surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f; - for ( i=0 ; itexinfo; + mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3]; + mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3]; + for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3) + { + for (j = 0;j < 2;j++) + { + val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3]; + if (mins[j] > val) + mins[j] = val; + if (maxs[j] < val) + maxs[j] = val; + } + } + for (i = 0;i < 2;i++) { - out->position[0] = LittleFloat (in->point[0]); - out->position[1] = LittleFloat (in->point[1]); - out->position[2] = LittleFloat (in->point[2]); + surf->texturemins[i] = (int) floor(mins[i] / 16) * 16; + surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i]; } } -/* -================= -Mod_LoadSubmodels -================= -*/ -static void Mod_LoadSubmodels (lump_t *l) +static void Mod_Q1BSP_LoadFaces(lump_t *l) { - dmodel_t *in; - dmodel_t *out; - int i, j, count; + dface_t *in; + msurface_t *surf; + int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes; + surfmesh_t *mesh; + float s, t; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t)); + + loadmodel->brushq1.numsurfaces = count; + loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int)); + loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int)); + loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int)); + + 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++) + { + surf->number = surfnum; + // FIXME: validate edges, texinfo, etc? + firstedge = LittleLong(in->firstedge); + numedges = LittleShort(in->numedges); + 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) + Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges); + i = LittleShort(in->texinfo); + if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo) + Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo); + surf->texinfo = loadmodel->brushq1.texinfo + i; + surf->flags = surf->texinfo->texture->flags; + + planenum = LittleShort(in->planenum); + if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes) + Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes); + + if (LittleShort(in->side)) + surf->flags |= SURF_PLANEBACK; + + surf->plane = loadmodel->brushq1.planes + planenum; - loadmodel->submodels = out; - loadmodel->numsubmodels = count; + // clear lightmap (filled in later) + surf->lightmaptexture = NULL; + + // force lightmap upload on first time seeing the surface + surf->cached_dlight = true; + + Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges); + + ssize = (surf->extents[0] >> 4) + 1; + tsize = (surf->extents[1] >> 4) + 1; + + // lighting info + for (i = 0;i < MAXLIGHTMAPS;i++) + surf->styles[i] = in->styles[i]; + i = LittleLong(in->lightofs); + if (i == -1) + surf->samples = NULL; + else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30) + surf->samples = loadmodel->brushq1.lightdata + i; + else // LordHavoc: white lighting (bsp version 29) + surf->samples = loadmodel->brushq1.lightdata + (i * 3); + + if (surf->texinfo->texture->shader == &Cshader_wall_lightmap) + { + if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256)) + Host_Error("Bad surface extents"); + // stainmap for permanent marks on walls + surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3); + // clear to white + memset(surf->stainsamples, 255, ssize * tsize * 3); + } + } + + loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris); + + 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++) + { + mesh = &surf->mesh; + mesh->num_vertices = surf->poly_numverts; + mesh->num_triangles = surf->poly_numverts - 2; + mesh->data_vertex3f = loadmodel->brushq1.entiremesh->data_vertex3f + totalverts * 3; + mesh->data_texcoordtexture2f = loadmodel->brushq1.entiremesh->data_texcoordtexture2f + totalverts * 2; + mesh->data_texcoordlightmap2f = loadmodel->brushq1.entiremesh->data_texcoordlightmap2f + totalverts * 2; + mesh->data_texcoorddetail2f = loadmodel->brushq1.entiremesh->data_texcoorddetail2f + totalverts * 2; + mesh->data_svector3f = loadmodel->brushq1.entiremesh->data_svector3f + totalverts * 3; + mesh->data_tvector3f = loadmodel->brushq1.entiremesh->data_tvector3f + totalverts * 3; + mesh->data_normal3f = loadmodel->brushq1.entiremesh->data_normal3f + totalverts * 3; + mesh->data_lightmapoffsets = loadmodel->brushq1.entiremesh->data_lightmapoffsets + totalverts; + mesh->data_element3i = loadmodel->brushq1.entiremesh->data_element3i + totaltris * 3; + mesh->data_neighbor3i = loadmodel->brushq1.entiremesh->data_neighbor3i + totaltris * 3; + + surf->lightmaptexturestride = 0; + surf->lightmaptexture = NULL; + + for (i = 0;i < mesh->num_vertices;i++) + { + mesh->data_vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0]; + mesh->data_vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1]; + mesh->data_vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2]; + s = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]; + t = DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]; + mesh->data_texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width; + mesh->data_texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height; + mesh->data_texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f); + mesh->data_texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f); + mesh->data_texcoordlightmap2f[i * 2 + 0] = 0; + mesh->data_texcoordlightmap2f[i * 2 + 1] = 0; + mesh->data_lightmapoffsets[i] = 0; + } + + for (i = 0;i < mesh->num_triangles;i++) + { + mesh->data_element3i[i * 3 + 0] = 0; + mesh->data_element3i[i * 3 + 1] = i + 1; + mesh->data_element3i[i * 3 + 2] = i + 2; + } + + Mod_BuildTriangleNeighbors(mesh->data_neighbor3i, mesh->data_element3i, mesh->num_triangles); + 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); + + if (surf->texinfo->texture->shader == &Cshader_wall_lightmap) + { + int i, iu, iv, smax, tmax; + float u, v, ubase, vbase, uscale, vscale; + + smax = surf->extents[0] >> 4; + tmax = surf->extents[1] >> 4; + + surf->flags |= SURF_LIGHTMAP; + if (r_miplightmaps.integer) + { + surf->lightmaptexturestride = smax+1; + 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); + } + else + { + surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0); + 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); + } + R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale); + uscale = (uscale - ubase) / (smax + 1); + vscale = (vscale - vbase) / (tmax + 1); + + for (i = 0;i < mesh->num_vertices;i++) + { + u = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0); + v = ((DotProduct((mesh->data_vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0); + mesh->data_texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase; + mesh->data_texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase; + // LordHavoc: calc lightmap data offset for vertex lighting to use + iu = (int) u; + iv = (int) v; + mesh->data_lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3; + } + } + } +} + +static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents < 0) + return; + Mod_Q1BSP_SetParent(node->children[0], node); + Mod_Q1BSP_SetParent(node->children[1], node); +} + +static void Mod_Q1BSP_LoadNodes(lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + + loadmodel->brushq1.nodes = out; + loadmodel->brushq1.numnodes = count; + + for ( i=0 ; imins[j] = LittleShort(in->mins[j]); + out->maxs[j] = LittleShort(in->maxs[j]); + } + + p = LittleLong(in->planenum); + out->plane = loadmodel->brushq1.planes + p; + + out->firstsurface = LittleShort(in->firstface); + out->numsurfaces = LittleShort(in->numfaces); + + for (j=0 ; j<2 ; j++) + { + p = LittleShort(in->children[j]); + if (p >= 0) + out->children[j] = loadmodel->brushq1.nodes + p; + else + out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p)); + } + } + + Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs +} + +static void Mod_Q1BSP_LoadLeafs(lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count, p, pvschainbytes; + qbyte *pvs; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + + loadmodel->brushq1.leafs = out; + loadmodel->brushq1.numleafs = count; + pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3; + loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes); + + for ( i=0 ; imins[j] = LittleShort(in->mins[j]); + out->maxs[j] = LittleShort(in->maxs[j]); + } + + // FIXME: this function could really benefit from some error checking + + out->contents = LittleLong(in->contents); + + out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface); + out->nummarksurfaces = LittleShort(in->nummarksurfaces); + if (out->firstmarksurface < 0 || LittleShort(in->firstmarksurface) + out->nummarksurfaces > loadmodel->brushq1.nummarksurfaces) + { + 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); + out->firstmarksurface = NULL; + out->nummarksurfaces = 0; + } + + out->pvsdata = pvs; + memset(out->pvsdata, 0xFF, pvschainbytes); + pvs += pvschainbytes; + + p = LittleLong(in->visofs); + if (p >= 0) + { + if (p >= loadmodel->brushq1.num_compressedpvs) + Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n"); + else + Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes); + } + + for (j = 0;j < 4;j++) + out->ambient_sound_level[j] = in->ambient_level[j]; + + // FIXME: Insert caustics here + } +} + +static void Mod_Q1BSP_LoadClipnodes(lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + + loadmodel->brushq1.clipnodes = out; + loadmodel->brushq1.numclipnodes = count; + + if (loadmodel->brush.ishlbsp) + { + hull = &loadmodel->brushq1.hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->brushq1.planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -36; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 36; + VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); + + hull = &loadmodel->brushq1.hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->brushq1.planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -32; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 32; + VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); + + hull = &loadmodel->brushq1.hulls[3]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->brushq1.planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -18; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 18; + VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); + } + else + { + hull = &loadmodel->brushq1.hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->brushq1.planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 32; + VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); + + hull = &loadmodel->brushq1.hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count-1; + hull->planes = loadmodel->brushq1.planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -24; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 64; + VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); + } + + for (i=0 ; iplanenum = LittleLong(in->planenum); + out->children[0] = LittleShort(in->children[0]); + out->children[1] = LittleShort(in->children[1]); + if (out->children[0] >= count || out->children[1] >= count) + Host_Error("Corrupt clipping hull(out of range child)\n"); + } +} + +//Duplicate the drawing hull structure as a clipping hull +static void Mod_Q1BSP_MakeHull0(void) +{ + mnode_t *in; + dclipnode_t *out; + int i; + hull_t *hull; + + hull = &loadmodel->brushq1.hulls[0]; + + in = loadmodel->brushq1.nodes; + out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t)); + + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = loadmodel->brushq1.numnodes - 1; + hull->planes = loadmodel->brushq1.planes; + + for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++) + { + out->planenum = in->plane - loadmodel->brushq1.planes; + out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes; + out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes; + } +} + +static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l) +{ + int i, j; + short *in; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name); + loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in); + loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int)); + + for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++) + { + j = (unsigned) LittleShort(in[i]); + if (j >= loadmodel->brushq1.numsurfaces) + Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number"); + loadmodel->brushq1.marksurfaces[i] = j; + } +} + +static void Mod_Q1BSP_LoadSurfedges(lump_t *l) +{ + int i; + int *in; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name); + loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in); + loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int)); + + for (i = 0;i < loadmodel->brushq1.numsurfedges;i++) + loadmodel->brushq1.surfedges[i] = LittleLong(in[i]); +} + + +static void Mod_Q1BSP_LoadPlanes(lump_t *l) +{ + int i; + mplane_t *out; + dplane_t *in; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name); + + loadmodel->brushq1.numplanes = l->filelen / sizeof(*in); + loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out)); + + for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++) + { + out->normal[0] = LittleFloat(in->normal[0]); + out->normal[1] = LittleFloat(in->normal[1]); + out->normal[2] = LittleFloat(in->normal[2]); + out->dist = LittleFloat(in->dist); + + PlaneClassify(out); + } +} + +typedef struct portal_s +{ + mplane_t plane; + mnode_t *nodes[2]; // [0] = front side of plane + struct portal_s *next[2]; + winding_t *winding; + struct portal_s *chain; // all portals are linked into a list +} +portal_t; + +static portal_t *portalchain; + +/* +=========== +AllocPortal +=========== +*/ +static portal_t *AllocPortal(void) +{ + portal_t *p; + p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t)); + p->chain = portalchain; + portalchain = p; + return p; +} + +static void FreePortal(portal_t *p) +{ + Mem_Free(p); +} + +static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node) +{ + // calculate children first + if (node->children[0]->contents >= 0) + Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]); + if (node->children[1]->contents >= 0) + Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]); + + // make combined bounding box from children + node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]); + node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]); + node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]); + node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]); + node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]); + node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]); +} + +static void Mod_Q1BSP_FinalizePortals(void) +{ + int i, j, numportals, numpoints; + portal_t *p, *pnext; + mportal_t *portal; + mvertex_t *point; + mleaf_t *leaf, *endleaf; + winding_t *w; + + // recalculate bounding boxes for all leafs(because qbsp is very sloppy) + leaf = loadmodel->brushq1.leafs; + endleaf = leaf + loadmodel->brushq1.numleafs; + for (;leaf < endleaf;leaf++) + { + VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000); + VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000); + } + p = portalchain; + while (p) + { + if (p->winding) + { + for (i = 0;i < 2;i++) + { + leaf = (mleaf_t *)p->nodes[i]; + w = p->winding; + for (j = 0;j < w->numpoints;j++) + { + if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0]; + if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1]; + if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2]; + if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0]; + if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1]; + if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2]; + } + } + } + p = p->chain; + } + + Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes); + + // tally up portal and point counts + p = portalchain; + numportals = 0; + numpoints = 0; + while (p) + { + // note: this check must match the one below or it will usually corrupt memory + // 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 + if (p->winding && p->nodes[0] != p->nodes[1] + && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID + && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY) + { + numportals += 2; + numpoints += p->winding->numpoints * 2; + } + p = p->chain; + } + loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t)); + loadmodel->brushq1.numportals = numportals; + loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t)); + loadmodel->brushq1.numportalpoints = numpoints; + // clear all leaf portal chains + for (i = 0;i < loadmodel->brushq1.numleafs;i++) + loadmodel->brushq1.leafs[i].portals = NULL; + // process all portals in the global portal chain, while freeing them + portal = loadmodel->brushq1.portals; + point = loadmodel->brushq1.portalpoints; + p = portalchain; + portalchain = NULL; + while (p) + { + pnext = p->chain; + + if (p->winding) + { + // note: this check must match the one above or it will usually corrupt memory + // 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 + if (p->nodes[0] != p->nodes[1] + && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID + && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY) + { + // first make the back to front portal(forward portal) + portal->points = point; + portal->numpoints = p->winding->numpoints; + portal->plane.dist = p->plane.dist; + VectorCopy(p->plane.normal, portal->plane.normal); + portal->here = (mleaf_t *)p->nodes[1]; + portal->past = (mleaf_t *)p->nodes[0]; + // copy points + for (j = 0;j < portal->numpoints;j++) + { + VectorCopy(p->winding->points[j], point->position); + point++; + } + PlaneClassify(&portal->plane); + + // link into leaf's portal chain + portal->next = portal->here->portals; + portal->here->portals = portal; + + // advance to next portal + portal++; + + // then make the front to back portal(backward portal) + portal->points = point; + portal->numpoints = p->winding->numpoints; + portal->plane.dist = -p->plane.dist; + VectorNegate(p->plane.normal, portal->plane.normal); + portal->here = (mleaf_t *)p->nodes[0]; + portal->past = (mleaf_t *)p->nodes[1]; + // copy points + for (j = portal->numpoints - 1;j >= 0;j--) + { + VectorCopy(p->winding->points[j], point->position); + point++; + } + PlaneClassify(&portal->plane); + + // link into leaf's portal chain + portal->next = portal->here->portals; + portal->here->portals = portal; + + // advance to next portal + portal++; + } + Winding_Free(p->winding); + } + FreePortal(p); + p = pnext; + } +} + +/* +============= +AddPortalToNodes +============= +*/ +static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back) +{ + if (!front) + Host_Error("AddPortalToNodes: NULL front node"); + if (!back) + Host_Error("AddPortalToNodes: NULL back node"); + if (p->nodes[0] || p->nodes[1]) + Host_Error("AddPortalToNodes: already included"); + // 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 + + p->nodes[0] = front; + p->next[0] = (portal_t *)front->portals; + front->portals = (mportal_t *)p; + + p->nodes[1] = back; + p->next[1] = (portal_t *)back->portals; + back->portals = (mportal_t *)p; +} + +/* +============= +RemovePortalFromNode +============= +*/ +static void RemovePortalFromNodes(portal_t *portal) +{ + int i; + mnode_t *node; + void **portalpointer; + portal_t *t; + for (i = 0;i < 2;i++) + { + node = portal->nodes[i]; + + portalpointer = (void **) &node->portals; + while (1) + { + t = *portalpointer; + if (!t) + Host_Error("RemovePortalFromNodes: portal not in leaf"); + + if (t == portal) + { + if (portal->nodes[0] == node) + { + *portalpointer = portal->next[0]; + portal->nodes[0] = NULL; + } + else if (portal->nodes[1] == node) + { + *portalpointer = portal->next[1]; + portal->nodes[1] = NULL; + } + else + Host_Error("RemovePortalFromNodes: portal not bounding leaf"); + break; + } + + if (t->nodes[0] == node) + portalpointer = (void **) &t->next[0]; + else if (t->nodes[1] == node) + portalpointer = (void **) &t->next[1]; + else + Host_Error("RemovePortalFromNodes: portal not bounding leaf"); + } + } +} + +static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node) +{ + int side; + mnode_t *front, *back, *other_node; + mplane_t clipplane, *plane; + portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp; + winding_t *nodeportalwinding, *frontwinding, *backwinding; + + // if a leaf, we're done + if (node->contents) + return; + + plane = node->plane; + + front = node->children[0]; + back = node->children[1]; + if (front == back) + Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy"); + + // create the new portal by generating a polygon for the node plane, + // and clipping it by all of the other portals(which came from nodes above this one) + nodeportal = AllocPortal(); + nodeportal->plane = *plane; + + nodeportalwinding = Winding_NewFromPlane(nodeportal->plane.normal[0], nodeportal->plane.normal[1], nodeportal->plane.normal[2], nodeportal->plane.dist); + side = 0; // shut up compiler warning + for (portal = (portal_t *)node->portals;portal;portal = portal->next[side]) + { + clipplane = portal->plane; + if (portal->nodes[0] == portal->nodes[1]) + Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)"); + if (portal->nodes[0] == node) + side = 0; + else if (portal->nodes[1] == node) + { + clipplane.dist = -clipplane.dist; + VectorNegate(clipplane.normal, clipplane.normal); + side = 1; + } + else + Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal"); + + nodeportalwinding = Winding_Clip(nodeportalwinding, clipplane.normal[0], clipplane.normal[1], clipplane.normal[2], clipplane.dist, true); + if (!nodeportalwinding) + { + Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n"); + break; + } + } + + if (nodeportalwinding) + { + // if the plane was not clipped on all sides, there was an error + nodeportal->winding = nodeportalwinding; + AddPortalToNodes(nodeportal, front, back); + } + + // split the portals of this node along this node's plane and assign them to the children of this node + // (migrating the portals downward through the tree) + for (portal = (portal_t *)node->portals;portal;portal = nextportal) + { + if (portal->nodes[0] == portal->nodes[1]) + Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)"); + if (portal->nodes[0] == node) + side = 0; + else if (portal->nodes[1] == node) + side = 1; + else + Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal"); + nextportal = portal->next[side]; + + other_node = portal->nodes[!side]; + RemovePortalFromNodes(portal); + + // cut the portal into two portals, one on each side of the node plane + Winding_Divide(portal->winding, plane->normal[0], plane->normal[1], plane->normal[2], plane->dist, &frontwinding, &backwinding); + + if (!frontwinding) + { + if (side == 0) + AddPortalToNodes(portal, back, other_node); + else + AddPortalToNodes(portal, other_node, back); + continue; + } + if (!backwinding) + { + if (side == 0) + AddPortalToNodes(portal, front, other_node); + else + AddPortalToNodes(portal, other_node, front); + continue; + } + + // the winding is split + splitportal = AllocPortal(); + temp = splitportal->chain; + *splitportal = *portal; + splitportal->chain = temp; + splitportal->winding = backwinding; + Winding_Free(portal->winding); + portal->winding = frontwinding; + + if (side == 0) + { + AddPortalToNodes(portal, front, other_node); + AddPortalToNodes(splitportal, back, other_node); + } + else + { + AddPortalToNodes(portal, other_node, front); + AddPortalToNodes(splitportal, other_node, back); + } + } + + Mod_Q1BSP_RecursiveNodePortals(front); + Mod_Q1BSP_RecursiveNodePortals(back); +} + +static void Mod_Q1BSP_MakePortals(void) +{ + portalchain = NULL; + Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes); + Mod_Q1BSP_FinalizePortals(); +} - for ( i=0 ; ineighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *)); + for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++) { - for (j=0 ; j<3 ; j++) + 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) { - // spread the mins / maxs by a pixel - out->mins[j] = LittleFloat (in->mins[j]) - 1; - out->maxs[j] = LittleFloat (in->maxs[j]) + 1; - out->origin[j] = LittleFloat (in->origin[j]); + if (surf->neighborsurfaces[vertnum]) + continue; + surf->neighborsurfaces[vertnum] = NULL; + for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++) + { + if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1) + || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1) + || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1) + || s == surf) + continue; + for (vnum = 0;vnum < s->poly_numverts;vnum++) + if (s->neighborsurfaces[vnum] == surf) + break; + if (vnum < s->poly_numverts) + continue; + 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) + { + if (s->neighborsurfaces[vnum] == NULL + && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2]) + || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2]))) + { + surf->neighborsurfaces[vertnum] = s; + s->neighborsurfaces[vnum] = surf; + break; + } + } + if (vnum < s->poly_numverts) + break; + } } - for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); - out->visleafs = LittleLong (in->visleafs); - out->firstface = LittleLong (in->firstface); - out->numfaces = LittleLong (in->numfaces); } +#endif } -/* -================= -Mod_LoadEdges -================= -*/ -static void Mod_LoadEdges (lump_t *l) +static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model) { - dedge_t *in; - medge_t *out; - int i, count; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - - loadmodel->edges = out; - loadmodel->numedges = count; - - for ( i=0 ; ibrushq1.nummodelsurfaces;i++) { - out->v[0] = (unsigned short)LittleShort(in->v[0]); - out->v[1] = (unsigned short)LittleShort(in->v[1]); + surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i; + for (j = 0;j < MAXLIGHTMAPS;j++) + stylecounts[surf->styles[j]]++; + } + totalcount = 0; + model->brushq1.light_styles = 0; + for (i = 0;i < 255;i++) + { + if (stylecounts[i]) + { + remapstyles[i] = model->brushq1.light_styles++; + totalcount += stylecounts[i] + 1; + } + } + if (!totalcount) + return; + model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte)); + model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int)); + model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **)); + model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *)); + model->brushq1.light_styles = 0; + for (i = 0;i < 255;i++) + if (stylecounts[i]) + model->brushq1.light_style[model->brushq1.light_styles++] = i; + j = 0; + for (i = 0;i < model->brushq1.light_styles;i++) + { + model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j; + j += stylecounts[model->brushq1.light_style[i]] + 1; + } + for (i = 0;i < model->brushq1.nummodelsurfaces;i++) + { + surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i; + for (j = 0;j < MAXLIGHTMAPS;j++) + if (surf->styles[j] != 255) + *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf; + } + j = 0; + for (i = 0;i < model->brushq1.light_styles;i++) + { + *model->brushq1.light_styleupdatechains[i] = NULL; + model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j; + j += stylecounts[model->brushq1.light_style[i]] + 1; } } -/* -================= -Mod_LoadTexinfo -================= -*/ -static void Mod_LoadTexinfo (lump_t *l) +static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model) { - texinfo_t *in; - mtexinfo_t *out; - int i, j, k, count, miptex; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - count = l->filelen / sizeof(*in); - out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - - loadmodel->texinfo = out; - loadmodel->numtexinfo = count; - - for (i = 0;i < count;i++, in++, out++) + int i, j; + for (i = 0;i < model->brushq1.numtextures;i++) + model->brushq1.pvstexturechainslength[i] = 0; + for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++) { - for (k = 0;k < 2;k++) - for (j = 0;j < 4;j++) - out->vecs[k][j] = LittleFloat (in->vecs[k][j]); - - miptex = LittleLong (in->miptex); - out->flags = LittleLong (in->flags); - - out->texture = NULL; - if (loadmodel->textures) + if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount) { - if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures) - Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures); - else - out->texture = loadmodel->textures + miptex; + model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j; + model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++; } - if (out->flags & TEX_SPECIAL) + } + for (i = 0, j = 0;i < model->brushq1.numtextures;i++) + { + if (model->brushq1.pvstexturechainslength[i]) { - // if texture chosen is NULL or the shader needs a lightmap, - // force to notexture water shader - if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP) - out->texture = loadmodel->textures + (loadmodel->numtextures - 1); + model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j; + j += model->brushq1.pvstexturechainslength[i] + 1; } else + model->brushq1.pvstexturechains[i] = NULL; + } + for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++) + if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount) + *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j; + for (i = 0;i < model->brushq1.numtextures;i++) + { + if (model->brushq1.pvstexturechainslength[i]) { - // if texture chosen is NULL, force to notexture - if (out->texture == NULL) - out->texture = loadmodel->textures + (loadmodel->numtextures - 2); + *model->brushq1.pvstexturechains[i] = NULL; + model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i]; } } } -/* -================ -CalcSurfaceExtents - -Fills in s->texturemins[] and s->extents[] -================ -*/ -static void CalcSurfaceExtents (msurface_t *s) +static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node) { - float mins[2], maxs[2], val; - int i,j, e; - mvertex_t *v; - mtexinfo_t *tex; - int bmins[2], bmaxs[2]; - - mins[0] = mins[1] = 999999999; - maxs[0] = maxs[1] = -999999999; - - tex = s->texinfo; + int i; + float d; - for (i=0 ; inumedges ; i++) + while (node->contents >= 0) { - e = loadmodel->surfedges[s->firstedge+i]; - if (e >= 0) - v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + d = PlaneDiff(org, node->plane); + if (d > radius) + node = node->children[0]; + else if (d < -radius) + node = node->children[1]; else - v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; - - for (j=0 ; j<2 ; j++) { - val = v->position[0] * tex->vecs[j][0] + - v->position[1] * tex->vecs[j][1] + - v->position[2] * tex->vecs[j][2] + - tex->vecs[j][3]; - if (val < mins[j]) - mins[j] = val; - if (val > maxs[j]) - maxs[j] = val; + // go down both sides + Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]); + node = node->children[1]; } } + // FIXME: code! + // if this is a leaf, accumulate the pvs bits + if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata) + for (i = 0;i < pvsbytes;i++) + pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i]; +} - for (i=0 ; i<2 ; i++) - { - bmins[i] = floor(mins[i]/16); - bmaxs[i] = ceil(maxs[i]/16); - - s->texturemins[i] = bmins[i] * 16; - s->extents[i] = (bmaxs[i] - bmins[i]) * 16; - } +//Calculates a PVS that is the inclusive or of all leafs within radius pixels +//of the given point. +static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength) +{ + int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3; + bytes = min(bytes, pvsbufferlength); + memset(pvsbuffer, 0, bytes); + Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq1.nodes); + return bytes; } +//Returns PVS data for a given point +//(note: always returns valid data, never NULL) +static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p) +{ + mnode_t *node; + Mod_CheckLoaded(model); + // LordHavoc: modified to start at first clip node, + // in other words: first node of the (sub)model + node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode; + while (node->contents == 0) + node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist]; + return ((mleaf_t *)node)->pvsdata; +} -void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs) +static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs) { - int i, j; - float *v; + vec3_t size; + const hull_t *hull; - mins[0] = mins[1] = mins[2] = 9999; - maxs[0] = maxs[1] = maxs[2] = -9999; - v = verts; - for (i = 0;i < numverts;i++) + VectorSubtract(inmaxs, inmins, size); + if (cmodel->brush.ishlbsp) { - for (j = 0;j < 3;j++, v++) + if (size[0] < 3) + hull = &cmodel->brushq1.hulls[0]; // 0x0x0 + else if (size[0] <= 32) { - if (*v < mins[j]) - mins[j] = *v; - if (*v > maxs[j]) - maxs[j] = *v; + if (size[2] < 54) // pick the nearest of 36 or 72 + hull = &cmodel->brushq1.hulls[3]; // 32x32x36 + else + hull = &cmodel->brushq1.hulls[1]; // 32x32x72 } + else + hull = &cmodel->brushq1.hulls[2]; // 64x64x64 + } + else + { + if (size[0] < 3) + hull = &cmodel->brushq1.hulls[0]; // 0x0x0 + else if (size[0] <= 32) + hull = &cmodel->brushq1.hulls[1]; // 32x32x56 + else + hull = &cmodel->brushq1.hulls[2]; // 64x64x88 } + VectorCopy(inmins, outmins); + VectorAdd(inmins, hull->clip_size, outmaxs); } -#if 0 -#define MAX_SUBDIVPOLYTRIANGLES 4096 -#define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3) +extern void R_Model_Brush_DrawSky(entity_render_t *ent); +extern void R_Model_Brush_Draw(entity_render_t *ent); +extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius); +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); +void Mod_Q1BSP_Load(model_t *mod, void *buffer) +{ + int i, j, k; + dheader_t *header; + dmodel_t *bm; + mempool_t *mainmempool; + char *loadname; + model_t *originalloadmodel; + float dist, modelyawradius, modelradius, *vec; + msurface_t *surf; -static int subdivpolyverts, subdivpolytriangles; -static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3]; -static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3]; + mod->type = mod_brush; -static int subdivpolylookupvert(vec3_t v) -{ - int i; - for (i = 0;i < subdivpolyverts;i++) - if (subdivpolyvert[i][0] == v[0] - && subdivpolyvert[i][1] == v[1] - && subdivpolyvert[i][2] == v[2]) - return i; - if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS) - Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size"); - VectorCopy(v, subdivpolyvert[subdivpolyverts]); - return subdivpolyverts++; -} + header = (dheader_t *)buffer; -static void SubdividePolygon (int numverts, float *verts) -{ - int i, i1, i2, i3, f, b, c, p; - vec3_t mins, maxs, front[256], back[256]; - float m, *pv, *cv, dist[256], frac; + i = LittleLong(header->version); + if (i != BSPVERSION && i != 30) + Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION); + mod->brush.ishlbsp = i == 30; + + mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents; + mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents; + mod->brush.GetPVS = Mod_Q1BSP_GetPVS; + mod->brush.FatPVS = Mod_Q1BSP_FatPVS; + mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS; + mod->brush.LightPoint = Mod_Q1BSP_LightPoint; + mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation; + mod->brush.TraceBox = Mod_Q1BSP_TraceBox; + mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint; + mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize; + mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf; + mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains; - if (numverts > 250) - Host_Error ("SubdividePolygon: ran out of verts in buffer"); + if (loadmodel->isworldmodel) + { + Cvar_SetValue("halflifebsp", mod->brush.ishlbsp); + // until we get a texture for it... + R_ResetQuakeSky(); + } + +// swap all the lumps + mod_base = (qbyte *)header; - BoundPoly (numverts, verts, mins, maxs); + for (i = 0;i < (int) sizeof(dheader_t) / 4;i++) + ((int *)header)[i] = LittleLong(((int *)header)[i]); - for (i = 0;i < 3;i++) +// load into heap + + // store which lightmap format to use + mod->brushq1.lightmaprgba = r_lightmaprgba.integer; + + Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]); + Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]); + Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]); + Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]); + Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]); + Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]); + Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]); + Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]); + Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]); + Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]); + Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]); + Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]); + Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]); + Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]); + Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]); + + if (mod->brushq1.data_compressedpvs) + Mem_Free(mod->brushq1.data_compressedpvs); + mod->brushq1.data_compressedpvs = NULL; + mod->brushq1.num_compressedpvs = 0; + + Mod_Q1BSP_MakeHull0(); + Mod_Q1BSP_MakePortals(); + + if (developer.integer) + 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); + + mod->numframes = 2; // regular and alternate animation + + mainmempool = mod->mempool; + loadname = mod->name; + + Mod_Q1BSP_LoadLightList(); + originalloadmodel = loadmodel; + +// +// set up the submodels(FIXME: this is confusing) +// + for (i = 0;i < mod->brush.numsubmodels;i++) { - m = (mins[i] + maxs[i]) * 0.5; - m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5); - if (maxs[i] - m < 8) - continue; - if (m - mins[i] < 8) - continue; + bm = &mod->brushq1.submodels[i]; - // cut it - for (cv = verts, c = 0;c < numverts;c++, cv += 3) - dist[c] = cv[i] - m; + mod->brushq1.hulls[0].firstclipnode = bm->headnode[0]; + for (j=1 ; jbrushq1.hulls[j].firstclipnode = bm->headnode[j]; + mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1; + } - f = b = 0; - for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3) + mod->brushq1.firstmodelsurface = bm->firstface; + mod->brushq1.nummodelsurfaces = bm->numfaces; + + // this gets altered below if sky is used + mod->DrawSky = NULL; + mod->Draw = R_Model_Brush_Draw; + mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume; + mod->DrawLight = R_Model_Brush_DrawLight; + mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **)); + mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *)); + mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int)); + Mod_Q1BSP_BuildPVSTextureChains(mod); + Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod); + if (mod->brushq1.nummodelsurfaces) { - if (dist[p] >= 0) - { - VectorCopy (pv, front[f]); - f++; - } - if (dist[p] <= 0) - { - VectorCopy (pv, back[b]); - b++; - } - if (dist[p] == 0 || dist[c] == 0) - continue; - if ( (dist[p] > 0) != (dist[c] > 0) ) + // LordHavoc: calculate bmodel bounding box rather than trusting what it says + mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f; + mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f; + modelyawradius = 0; + modelradius = 0; + for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++) { - // clip point - frac = dist[p] / (dist[p] - dist[c]); - front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]); - front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]); - front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]); - f++; - b++; + // we only need to have a drawsky function if it is used(usually only on world model) + if (surf->texinfo->texture->shader == &Cshader_sky) + mod->DrawSky = R_Model_Brush_DrawSky; + // LordHavoc: submodels always clip, even if water + if (mod->brush.numsubmodels - 1) + surf->flags |= SURF_SOLIDCLIP; + // calculate bounding shapes + for (k = 0, vec = surf->mesh.data_vertex3f;k < surf->mesh.num_vertices;k++, vec += 3) + { + if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0]; + if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1]; + if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2]; + if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0]; + if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1]; + if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2]; + dist = vec[0]*vec[0]+vec[1]*vec[1]; + if (modelyawradius < dist) + modelyawradius = dist; + dist += vec[2]*vec[2]; + if (modelradius < dist) + modelradius = dist; + } } + modelyawradius = sqrt(modelyawradius); + modelradius = sqrt(modelradius); + mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius); + mod->yawmins[2] = mod->normalmins[2]; + mod->yawmaxs[2] = mod->normalmaxs[2]; + mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius; + mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius; + mod->radius = modelradius; + mod->radius2 = modelradius * modelradius; } - - SubdividePolygon (f, front[0]); - SubdividePolygon (b, back[0]); - return; - } - - i1 = subdivpolylookupvert(verts); - i2 = subdivpolylookupvert(verts + 3); - for (i = 2;i < numverts;i++) - { - if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES) + else { - Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n"); - return; + // LordHavoc: empty submodel(lacrima.bsp has such a glitch) + Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname); } + Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool); - i3 = subdivpolylookupvert(verts + i * 3); - subdivpolyindex[subdivpolytriangles][0] = i1; - subdivpolyindex[subdivpolytriangles][1] = i2; - subdivpolyindex[subdivpolytriangles][2] = i3; - i2 = i3; - subdivpolytriangles++; + mod->brushq1.visleafs = bm->visleafs; + + // LordHavoc: only register submodels if it is the world + // (prevents bsp models from replacing world submodels) + if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1)) + { + char name[10]; + // duplicate the basic information + sprintf(name, "*%i", i+1); + loadmodel = Mod_FindName(name); + *loadmodel = *mod; + strcpy(loadmodel->name, name); + // textures and memory belong to the main model + loadmodel->texturepool = NULL; + loadmodel->mempool = NULL; + mod = loadmodel; + } } -} -/* -================ -Mod_GenerateWarpMesh + loadmodel = originalloadmodel; + //Mod_Q1BSP_ProcessLightList(); +} -Breaks a polygon up along axial 64 unit -boundaries so that turbulent and sky warps -can be done reasonably. -================ -*/ -void Mod_GenerateWarpMesh (msurface_t *surf) +static void Mod_Q2BSP_LoadEntities(lump_t *l) { - int i, j; - surfvertex_t *v; - surfmesh_t *mesh; +} - subdivpolytriangles = 0; - subdivpolyverts = 0; - SubdividePolygon (surf->poly_numverts, surf->poly_verts); - if (subdivpolytriangles < 1) - Host_Error("Mod_GenerateWarpMesh: no triangles?\n"); +static void Mod_Q2BSP_LoadPlanes(lump_t *l) +{ +/* + d_t *in; + m_t *out; + int i, count; - surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t)); - mesh->numverts = subdivpolyverts; - mesh->numtriangles = subdivpolytriangles; - mesh->vertex = (surfvertex_t *)(mesh + 1); - mesh->index = (int *)(mesh->vertex + mesh->numverts); - memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t)); + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - for (i = 0;i < mesh->numtriangles;i++) - for (j = 0;j < 3;j++) - mesh->index[i*3+j] = subdivpolyindex[i][j]; + loadmodel-> = out; + loadmodel->num = count; - for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++) + for (i = 0;i < count;i++, in++, out++) { - VectorCopy(subdivpolyvert[i], v->v); - v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]); - v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]); } +*/ } -#endif -void Mod_GenerateWallMesh (msurface_t *surf, int vertexonly) +static void Mod_Q2BSP_LoadVertices(lump_t *l) { - int i, iu, iv, *index, smax, tmax; - float *in, s, t, u, v, ubase, vbase, uscale, vscale, normal[3]; - surfmesh_t *mesh; +/* + d_t *in; + m_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - smax = surf->extents[0] >> 4; - tmax = surf->extents[1] >> 4; + loadmodel-> = out; + loadmodel->num = count; - if (vertexonly) + for (i = 0;i < count;i++, in++, out++) { - surf->lightmaptexturestride = 0; - surf->lightmaptexture = NULL; - uscale = 0; - vscale = 0; - ubase = 0; - vbase = 0; } - else - { - surf->flags |= SURF_LIGHTMAP; - if (r_miplightmaps.integer) - { - surf->lightmaptexturestride = (surf->extents[0]>>4)+1; - surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_PRECACHE); - } - else - { - surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0); - surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE); - } - R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale); - uscale = (uscale - ubase) * 16.0 / ((surf->extents[0] & ~15) + 16); - vscale = (vscale - vbase) * 16.0 / ((surf->extents[1] & ~15) + 16); - } - - surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[6]) + surf->poly_numverts * (4 + 2 + 2 + 2 + 1 + 3) * sizeof(float)); - mesh->numverts = surf->poly_numverts; - mesh->numtriangles = surf->poly_numverts - 2; - mesh->verts = (float *)(mesh + 1); - mesh->st = mesh->verts + mesh->numverts * 4; - mesh->uv = mesh->st + mesh->numverts * 2; - mesh->ab = mesh->uv + mesh->numverts * 2; - mesh->lightmapoffsets = (int *)(mesh->ab + mesh->numverts * 2); - mesh->index = mesh->lightmapoffsets + mesh->numverts; - mesh->triangleneighbors = mesh->index + mesh->numtriangles * 3; - mesh->normals = (float *)(mesh->triangleneighbors + mesh->numtriangles * 3); - - index = mesh->index; - for (i = 0;i < mesh->numtriangles;i++) - { - *index++ = 0; - *index++ = i + 1; - *index++ = i + 2; - } - Mod_BuildTriangleNeighbors(mesh->triangleneighbors, mesh->index, mesh->numtriangles); - - VectorCopy(surf->plane->normal, normal); - if (surf->flags & SURF_PLANEBACK) - VectorNegate(normal, normal); - for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3) - { - s = DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]; - t = DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]; - u = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0); - v = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0); - // LordHavoc: calc lightmap data offset for vertex lighting to use - iu = (int) u; - iv = (int) v; - iu = bound(0, iu, smax); - iv = bound(0, iv, tmax); - u = u * uscale + ubase; - v = v * vscale + vbase; - - mesh->verts[i * 4 + 0] = in[0]; - mesh->verts[i * 4 + 1] = in[1]; - mesh->verts[i * 4 + 2] = in[2]; - mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width; - mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height; - mesh->uv[i * 2 + 0] = u; - mesh->uv[i * 2 + 1] = v; - mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f); - mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f); - mesh->lightmapoffsets[i] = ((iv * (smax+1) + iu) * 3); - mesh->normals[i * 3 + 0] = normal[0]; - mesh->normals[i * 3 + 1] = normal[1]; - mesh->normals[i * 3 + 2] = normal[2]; - } -} - -void Mod_GenerateVertexMesh (msurface_t *surf) -{ - int i, *index; - float *in, s, t, normal[3]; - surfmesh_t *mesh; +*/ +} - surf->lightmaptexturestride = 0; - surf->lightmaptexture = NULL; +static void Mod_Q2BSP_LoadVisibility(lump_t *l) +{ +/* + d_t *in; + m_t *out; + int i, count; - surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[6]) + surf->poly_numverts * (4 + 2 + 2 + 3) * sizeof(float)); - mesh->numverts = surf->poly_numverts; - mesh->numtriangles = surf->poly_numverts - 2; - mesh->verts = (float *)(mesh + 1); - mesh->st = mesh->verts + mesh->numverts * 4; - mesh->ab = mesh->st + mesh->numverts * 2; - mesh->index = (int *)(mesh->ab + mesh->numverts * 2); - mesh->triangleneighbors = mesh->index + mesh->numtriangles * 3; - mesh->normals = (float *)(mesh->triangleneighbors + mesh->numtriangles * 3); + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - index = mesh->index; - for (i = 0;i < mesh->numtriangles;i++) - { - *index++ = 0; - *index++ = i + 1; - *index++ = i + 2; - } - Mod_BuildTriangleNeighbors(mesh->triangleneighbors, mesh->index, mesh->numtriangles); + loadmodel-> = out; + loadmodel->num = count; - VectorCopy(surf->plane->normal, normal); - if (surf->flags & SURF_PLANEBACK) - VectorNegate(normal, normal); - for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3) + for (i = 0;i < count;i++, in++, out++) { - s = (DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]); - t = (DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]); - mesh->verts[i * 4 + 0] = in[0]; - mesh->verts[i * 4 + 1] = in[1]; - mesh->verts[i * 4 + 2] = in[2]; - mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width; - mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height; - mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f); - mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f); - mesh->normals[i * 3 + 0] = normal[0]; - mesh->normals[i * 3 + 1] = normal[1]; - mesh->normals[i * 3 + 2] = normal[2]; } +*/ } -void Mod_GenerateSurfacePolygon (msurface_t *surf) +static void Mod_Q2BSP_LoadNodes(lump_t *l) { - int i, lindex; - float *vec, *vert, mins[3], maxs[3], temp[3], dist; +/* + d_t *in; + m_t *out; + int i, count; - // convert edges back to a normal polygon - surf->poly_numverts = surf->numedges; - vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges); - for (i = 0;i < surf->numedges;i++) - { - lindex = loadmodel->surfedges[surf->firstedge + i]; - if (lindex > 0) - vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; - else - vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; - VectorCopy (vec, vert); - vert += 3; - } - vert = surf->poly_verts; - VectorCopy(vert, mins); - VectorCopy(vert, maxs); - vert += 3; - for (i = 1;i < surf->poly_numverts;i++) - { - if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0]; - if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1]; - if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2]; - vert += 3; - } - VectorCopy(mins, surf->poly_mins); - VectorCopy(maxs, surf->poly_maxs); - surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f; - surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f; - surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f; - surf->poly_radius2 = 0; - vert = surf->poly_verts; - for (i = 0;i < surf->poly_numverts;i++) + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + + loadmodel-> = out; + loadmodel->num = count; + + for (i = 0;i < count;i++, in++, out++) { - VectorSubtract(vert, surf->poly_center, temp); - dist = DotProduct(temp, temp); - if (surf->poly_radius2 < dist) - surf->poly_radius2 = dist; - vert += 3; } - surf->poly_radius = sqrt(surf->poly_radius2); +*/ } -/* -================= -Mod_LoadFaces -================= -*/ -static void Mod_LoadFaces (lump_t *l) +static void Mod_Q2BSP_LoadTexInfo(lump_t *l) { - dface_t *in; - msurface_t *out; - int i, count, surfnum, planenum, ssize, tsize; +/* + d_t *in; + m_t *out; + int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - loadmodel->surfaces = out; - loadmodel->numsurfaces = count; - loadmodel->surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int)); - loadmodel->surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int)); + loadmodel-> = out; + loadmodel->num = count; - for (surfnum = 0;surfnum < count;surfnum++, in++, out++) + for (i = 0;i < count;i++, in++, out++) { - out->number = surfnum; - // FIXME: validate edges, texinfo, etc? - out->firstedge = LittleLong(in->firstedge); - out->numedges = LittleShort(in->numedges); - if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges) - Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges); - - i = LittleShort (in->texinfo); - if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo) - Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo); - out->texinfo = loadmodel->texinfo + i; - out->flags = out->texinfo->texture->flags; - - planenum = LittleShort(in->planenum); - if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes) - Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes); - - if (LittleShort(in->side)) - out->flags |= SURF_PLANEBACK; - - out->plane = loadmodel->planes + planenum; - - // clear lightmap (filled in later) - out->lightmaptexture = NULL; - - // force lightmap upload on first time seeing the surface - out->cached_dlight = true; - - CalcSurfaceExtents (out); - - ssize = (out->extents[0] >> 4) + 1; - tsize = (out->extents[1] >> 4) + 1; - - // lighting info - for (i = 0;i < MAXLIGHTMAPS;i++) - out->styles[i] = in->styles[i]; - i = LittleLong(in->lightofs); - if (i == -1) - out->samples = NULL; - else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30) - out->samples = loadmodel->lightdata + i; - else // LordHavoc: white lighting (bsp version 29) - out->samples = loadmodel->lightdata + (i * 3); - - Mod_GenerateSurfacePolygon(out); - if (out->texinfo->texture->shader == &Cshader_wall_lightmap) - { - if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256)) - Host_Error ("Bad surface extents"); - Mod_GenerateWallMesh (out, false); - // stainmap for permanent marks on walls - out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3); - // clear to white - memset(out->stainsamples, 255, ssize * tsize * 3); - } - else - Mod_GenerateVertexMesh (out); } -} - -/* -================= -Mod_SetParent -================= */ -static void Mod_SetParent (mnode_t *node, mnode_t *parent) -{ - node->parent = parent; - if (node->contents < 0) - return; - Mod_SetParent (node->children[0], node); - Mod_SetParent (node->children[1], node); } -/* -================= -Mod_LoadNodes -================= -*/ -static void Mod_LoadNodes (lump_t *l) +static void Mod_Q2BSP_LoadFaces(lump_t *l) { - int i, j, count, p; - dnode_t *in; - mnode_t *out; +/* + d_t *in; + m_t *out; + int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - loadmodel->nodes = out; - loadmodel->numnodes = count; + loadmodel-> = out; + loadmodel->num = count; - for ( i=0 ; imins[j] = LittleShort (in->mins[j]); - out->maxs[j] = LittleShort (in->maxs[j]); - } - - p = LittleLong(in->planenum); - out->plane = loadmodel->planes + p; - - out->firstsurface = LittleShort (in->firstface); - out->numsurfaces = LittleShort (in->numfaces); - - for (j=0 ; j<2 ; j++) - { - p = LittleShort (in->children[j]); - if (p >= 0) - out->children[j] = loadmodel->nodes + p; - else - out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); - } } - - Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs +*/ } -/* -================= -Mod_LoadLeafs -================= -*/ -static void Mod_LoadLeafs (lump_t *l) +static void Mod_Q2BSP_LoadLighting(lump_t *l) { - dleaf_t *in; - mleaf_t *out; - int i, j, count, p; +/* + d_t *in; + m_t *out; + int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - loadmodel->leafs = out; - loadmodel->numleafs = count; + loadmodel-> = out; + loadmodel->num = count; - for ( i=0 ; imins[j] = LittleShort (in->mins[j]); - out->maxs[j] = LittleShort (in->maxs[j]); - } - - p = LittleLong(in->contents); - out->contents = p; - - out->firstmarksurface = loadmodel->marksurfaces + - LittleShort(in->firstmarksurface); - out->nummarksurfaces = LittleShort(in->nummarksurfaces); - - p = LittleLong(in->visofs); - if (p == -1) - out->compressed_vis = NULL; - else - out->compressed_vis = loadmodel->visdata + p; - - for (j=0 ; j<4 ; j++) - out->ambient_sound_level[j] = in->ambient_level[j]; - - // FIXME: Insert caustics here } +*/ } -/* -================= -Mod_LoadClipnodes -================= -*/ -static void Mod_LoadClipnodes (lump_t *l) +static void Mod_Q2BSP_LoadLeafs(lump_t *l) { - dclipnode_t *in, *out; - int i, count; - hull_t *hull; +/* + d_t *in; + m_t *out; + int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); + Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); - out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - loadmodel->clipnodes = out; - loadmodel->numclipnodes = count; + loadmodel-> = out; + loadmodel->num = count; - if (loadmodel->ishlbsp) + for (i = 0;i < count;i++, in++, out++) { - hull = &loadmodel->hulls[1]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -36; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 36; - VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); + } +*/ +} - hull = &loadmodel->hulls[2]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -32; - hull->clip_mins[1] = -32; - hull->clip_mins[2] = -32; - hull->clip_maxs[0] = 32; - hull->clip_maxs[1] = 32; - hull->clip_maxs[2] = 32; - VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); +static void Mod_Q2BSP_LoadLeafFaces(lump_t *l) +{ +/* + d_t *in; + m_t *out; + int i, count; - hull = &loadmodel->hulls[3]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -18; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 18; - VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); - } - else - { - hull = &loadmodel->hulls[1]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -16; - hull->clip_mins[1] = -16; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 16; - hull->clip_maxs[1] = 16; - hull->clip_maxs[2] = 32; - VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - hull = &loadmodel->hulls[2]; - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count-1; - hull->planes = loadmodel->planes; - hull->clip_mins[0] = -32; - hull->clip_mins[1] = -32; - hull->clip_mins[2] = -24; - hull->clip_maxs[0] = 32; - hull->clip_maxs[1] = 32; - hull->clip_maxs[2] = 64; - VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size); - } + loadmodel-> = out; + loadmodel->num = count; - for (i=0 ; iplanenum = LittleLong(in->planenum); - out->children[0] = LittleShort(in->children[0]); - out->children[1] = LittleShort(in->children[1]); - if (out->children[0] >= count || out->children[1] >= count) - Host_Error("Corrupt clipping hull (out of range child)\n"); + for (i = 0;i < count;i++, in++, out++) + { } +*/ } +static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l) +{ /* -================= -Mod_MakeHull0 + d_t *in; + m_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); -Duplicate the drawing hull structure as a clipping hull -================= + loadmodel-> = out; + loadmodel->num = count; + + for (i = 0;i < count;i++, in++, out++) + { + } */ -static void Mod_MakeHull0 (void) -{ - mnode_t *in; - dclipnode_t *out; - int i; - hull_t *hull; +} - hull = &loadmodel->hulls[0]; +static void Mod_Q2BSP_LoadEdges(lump_t *l) +{ +/* + d_t *in; + m_t *out; + int i, count; - in = loadmodel->nodes; - out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t)); + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = loadmodel->numnodes - 1; - hull->planes = loadmodel->planes; + loadmodel-> = out; + loadmodel->num = count; - for (i = 0;i < loadmodel->numnodes;i++, out++, in++) + for (i = 0;i < count;i++, in++, out++) { - out->planenum = in->plane - loadmodel->planes; - out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes; - out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes; } +*/ } -/* -================= -Mod_LoadMarksurfaces -================= -*/ -static void Mod_LoadMarksurfaces (lump_t *l) +static void Mod_Q2BSP_LoadSurfEdges(lump_t *l) { - int i, j; - short *in; +/* + d_t *in; + m_t *out; + int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - loadmodel->nummarksurfaces = l->filelen / sizeof(*in); - loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(int)); + Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + + loadmodel-> = out; + loadmodel->num = count; - for (i = 0;i < loadmodel->nummarksurfaces;i++) + for (i = 0;i < count;i++, in++, out++) { - j = (unsigned) LittleShort(in[i]); - if (j >= loadmodel->numsurfaces) - Host_Error ("Mod_ParseMarksurfaces: bad surface number"); - loadmodel->marksurfaces[i] = j; } +*/ } -/* -================= -Mod_LoadSurfedges -================= -*/ -static void Mod_LoadSurfedges (lump_t *l) +static void Mod_Q2BSP_LoadBrushes(lump_t *l) { - int i; - int *in; +/* + d_t *in; + m_t *out; + int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); - loadmodel->numsurfedges = l->filelen / sizeof(*in); - loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int)); - - for (i = 0;i < loadmodel->numsurfedges;i++) - loadmodel->surfedges[i] = LittleLong (in[i]); -} + Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + loadmodel-> = out; + loadmodel->num = count; -/* -================= -Mod_LoadPlanes -================= + for (i = 0;i < count;i++, in++, out++) + { + } */ -static void Mod_LoadPlanes (lump_t *l) +} + +static void Mod_Q2BSP_LoadBrushSides(lump_t *l) { - int i; - mplane_t *out; - dplane_t *in; +/* + d_t *in; + m_t *out; + int i, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) - Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name); + Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - loadmodel->numplanes = l->filelen / sizeof(*in); - loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out)); + loadmodel-> = out; + loadmodel->num = count; - for (i = 0;i < loadmodel->numplanes;i++, in++, out++) + for (i = 0;i < count;i++, in++, out++) { - out->normal[0] = LittleFloat (in->normal[0]); - out->normal[1] = LittleFloat (in->normal[1]); - out->normal[2] = LittleFloat (in->normal[2]); - out->dist = LittleFloat (in->dist); - - PlaneClassify(out); } +*/ } -#define MAX_POINTS_ON_WINDING 64 - -typedef struct +static void Mod_Q2BSP_LoadAreas(lump_t *l) { - int numpoints; - int padding; - double points[8][3]; // variable sized -} -winding_t; - /* -================== -NewWinding -================== -*/ -static winding_t *NewWinding (int points) -{ - winding_t *w; - int size; + d_t *in; + m_t *out; + int i, count; - if (points > MAX_POINTS_ON_WINDING) - Sys_Error("NewWinding: too many points\n"); + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - size = sizeof(winding_t) + sizeof(double[3]) * (points - 8); - w = Mem_Alloc(loadmodel->mempool, size); - memset (w, 0, size); + loadmodel-> = out; + loadmodel->num = count; - return w; + for (i = 0;i < count;i++, in++, out++) + { + } +*/ } -static void FreeWinding (winding_t *w) +static void Mod_Q2BSP_LoadAreaPortals(lump_t *l) { - Mem_Free(w); +/* + d_t *in; + m_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + + loadmodel-> = out; + loadmodel->num = count; + + for (i = 0;i < count;i++, in++, out++) + { + } +*/ } +static void Mod_Q2BSP_LoadModels(lump_t *l) +{ /* -================= -BaseWindingForPlane -================= + d_t *in; + m_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + + loadmodel-> = out; + loadmodel->num = count; + + for (i = 0;i < count;i++, in++, out++) + { + } */ -static winding_t *BaseWindingForPlane (mplane_t *p) -{ - double org[3], vright[3], vup[3], normal[3]; - winding_t *w; +} - VectorCopy(p->normal, normal); - VectorVectorsDouble(normal, vright, vup); +void static Mod_Q2BSP_Load(model_t *mod, void *buffer) +{ + int i; + q2dheader_t *header; - VectorScale (vup, 1024.0*1024.0*1024.0, vup); - VectorScale (vright, 1024.0*1024.0*1024.0, vright); + Host_Error("Mod_Q2BSP_Load: not yet implemented\n"); - // project a really big axis aligned box onto the plane - w = NewWinding (4); + mod->type = mod_brushq2; - VectorScale (p->normal, p->dist, org); + header = (q2dheader_t *)buffer; - VectorSubtract (org, vright, w->points[0]); - VectorAdd (w->points[0], vup, w->points[0]); + i = LittleLong(header->version); + if (i != Q2BSPVERSION) + Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION); + mod->brush.ishlbsp = false; + if (loadmodel->isworldmodel) + { + Cvar_SetValue("halflifebsp", mod->brush.ishlbsp); + // until we get a texture for it... + R_ResetQuakeSky(); + } - VectorAdd (org, vright, w->points[1]); - VectorAdd (w->points[1], vup, w->points[1]); + mod_base = (qbyte *)header; - VectorAdd (org, vright, w->points[2]); - VectorSubtract (w->points[2], vup, w->points[2]); + // swap all the lumps + for (i = 0;i < (int) sizeof(*header) / 4;i++) + ((int *)header)[i] = LittleLong(((int *)header)[i]); - VectorSubtract (org, vright, w->points[3]); - VectorSubtract (w->points[3], vup, w->points[3]); + // store which lightmap format to use + mod->brushq1.lightmaprgba = r_lightmaprgba.integer; + + Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]); + Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]); + Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]); + Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]); + Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]); + Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]); + Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]); + Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]); + Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]); + Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]); + Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]); + Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]); + Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]); + Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]); + Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]); + Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]); + Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]); + // LordHavoc: must go last because this makes the submodels + Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]); +} - w->numpoints = 4; +static int Mod_Q3BSP_SuperContentsFromNativeContents(model_t *model, int nativecontents); +static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents); - return w; +static void Mod_Q3BSP_LoadEntities(lump_t *l) +{ + const char *data; + char key[128], value[4096]; + float v[3]; + loadmodel->brushq3.num_lightgrid_cellsize[0] = 64; + loadmodel->brushq3.num_lightgrid_cellsize[1] = 64; + loadmodel->brushq3.num_lightgrid_cellsize[2] = 128; + if (!l->filelen) + return; + loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen); + memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen); + data = loadmodel->brush.entities; + // some Q3 maps override the lightgrid_cellsize with a worldspawn key + if (data && COM_ParseToken(&data, false) && com_token[0] == '{') + { + while (1) + { + if (!COM_ParseToken(&data, false)) + break; // error + if (com_token[0] == '}') + break; // end of worldspawn + if (com_token[0] == '_') + strcpy(key, com_token + 1); + else + strcpy(key, com_token); + while (key[strlen(key)-1] == ' ') // remove trailing spaces + key[strlen(key)-1] = 0; + if (!COM_ParseToken(&data, false)) + break; // error + strcpy(value, com_token); + if (!strcmp("gridsize", key)) + { + if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0) + VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize); + } + } + } } -/* -================== -ClipWinding - -Clips the winding to the plane, returning the new winding on the positive side -Frees the input winding. -If keepon is true, an exactly on-plane winding will be saved, otherwise -it will be clipped away. -================== -*/ -static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon) +static void Mod_Q3BSP_LoadTextures(lump_t *l) { - double dists[MAX_POINTS_ON_WINDING + 1]; - int sides[MAX_POINTS_ON_WINDING + 1]; - int counts[3]; - double dot; - int i, j; - double *p1, *p2; - double mid[3]; - winding_t *neww; - int maxpts; + q3dtexture_t *in; + q3mtexture_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; + loadmodel->brushq3.data_textures = out; + loadmodel->brushq3.num_textures = count; - // determine sides for each point - for (i = 0;i < in->numpoints;i++) + for (i = 0;i < count;i++, in++, out++) { - dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist; - if (dot > ON_EPSILON) - sides[i] = SIDE_FRONT; - else if (dot < -ON_EPSILON) - sides[i] = SIDE_BACK; - else - sides[i] = SIDE_ON; - counts[sides[i]]++; + strlcpy (out->name, in->name, sizeof (out->name)); + out->surfaceflags = LittleLong(in->surfaceflags); + out->nativecontents = LittleLong(in->contents); + out->supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, out->nativecontents); + out->renderflags = 0; + if (!strcmp(out->name, "caulk") || !strcmp(out->name, "common/caulk") || !strcmp(out->name, "textures/common/caulk")) + out->renderflags |= Q3MTEXTURERENDERFLAGS_NODRAW; + if (!strncmp(out->name, "textures/skies/", 15)) + out->renderflags |= Q3MTEXTURERENDERFLAGS_SKY; + + out->number = i; + Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true); } - sides[i] = sides[0]; - dists[i] = dists[0]; +} + +static void Mod_Q3BSP_LoadPlanes(lump_t *l) +{ + q3dplane_t *in; + mplane_t *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - if (keepon && !counts[0] && !counts[1]) - return in; + loadmodel->brushq3.data_planes = out; + loadmodel->brushq3.num_planes = count; - if (!counts[0]) + for (i = 0;i < count;i++, in++, out++) { - FreeWinding (in); - return NULL; + out->normal[0] = LittleLong(in->normal[0]); + out->normal[1] = LittleLong(in->normal[1]); + out->normal[2] = LittleLong(in->normal[2]); + out->dist = LittleLong(in->dist); + PlaneClassify(out); } - if (!counts[1]) - return in; +} + +static void Mod_Q3BSP_LoadBrushSides(lump_t *l) +{ + q3dbrushside_t *in; + q3mbrushside_t *out; + int i, n, count; - maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors - if (maxpts > MAX_POINTS_ON_WINDING) - Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING"); + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - neww = NewWinding (maxpts); + loadmodel->brushq3.data_brushsides = out; + loadmodel->brushq3.num_brushsides = count; - for (i = 0;i < in->numpoints;i++) + for (i = 0;i < count;i++, in++, out++) { - if (neww->numpoints >= maxpts) - Sys_Error ("ClipWinding: points exceeded estimate"); + n = LittleLong(in->planeindex); + if (n < 0 || n >= loadmodel->brushq3.num_planes) + Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes); + out->plane = loadmodel->brushq3.data_planes + n; + n = LittleLong(in->textureindex); + if (n < 0 || n >= loadmodel->brushq3.num_textures) + Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures); + out->texture = loadmodel->brushq3.data_textures + n; + } +} + +static void Mod_Q3BSP_LoadBrushes(lump_t *l) +{ + q3dbrush_t *in; + q3mbrush_t *out; + int i, j, n, c, count, maxplanes; + mplane_t *planes; - p1 = in->points[i]; + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - if (sides[i] == SIDE_ON) + loadmodel->brushq3.data_brushes = out; + loadmodel->brushq3.num_brushes = count; + + maxplanes = 0; + planes = NULL; + + for (i = 0;i < count;i++, in++, out++) + { + n = LittleLong(in->firstbrushside); + c = LittleLong(in->numbrushsides); + if (n < 0 || n + c > loadmodel->brushq3.num_brushsides) + Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides); + out->firstbrushside = loadmodel->brushq3.data_brushsides + n; + out->numbrushsides = c; + n = LittleLong(in->textureindex); + if (n < 0 || n >= loadmodel->brushq3.num_textures) + Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures); + out->texture = loadmodel->brushq3.data_textures + n; + + // make a list of mplane_t structs to construct a colbrush from + if (maxplanes < out->numbrushsides) { - VectorCopy (p1, neww->points[neww->numpoints]); - neww->numpoints++; - continue; + maxplanes = out->numbrushsides; + if (planes) + Mem_Free(planes); + planes = Mem_Alloc(tempmempool, sizeof(mplane_t) * maxplanes); } - - if (sides[i] == SIDE_FRONT) + for (j = 0;j < out->numbrushsides;j++) { - VectorCopy (p1, neww->points[neww->numpoints]); - neww->numpoints++; + VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal); + planes[j].dist = out->firstbrushside[j].plane->dist; } + // make the colbrush from the planes + out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents); + } + if (planes) + Mem_Free(planes); +} - if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) - continue; +static void Mod_Q3BSP_LoadEffects(lump_t *l) +{ + q3deffect_t *in; + q3meffect_t *out; + int i, n, count; - // generate a split point - p2 = in->points[(i+1)%in->numpoints]; + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - dot = dists[i] / (dists[i]-dists[i+1]); - for (j = 0;j < 3;j++) - { // avoid round off error when possible - if (split->normal[j] == 1) - mid[j] = split->dist; - else if (split->normal[j] == -1) - mid[j] = -split->dist; - else - mid[j] = p1[j] + dot*(p2[j]-p1[j]); - } + loadmodel->brushq3.data_effects = out; + loadmodel->brushq3.num_effects = count; - VectorCopy (mid, neww->points[neww->numpoints]); - neww->numpoints++; + for (i = 0;i < count;i++, in++, out++) + { + strlcpy (out->shadername, in->shadername, sizeof (out->shadername)); + n = LittleLong(in->brushindex); + if (n < 0 || n >= loadmodel->brushq3.num_brushes) + Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes); + out->brush = loadmodel->brushq3.data_brushes + n; + out->unknown = LittleLong(in->unknown); } +} - // free the original winding - FreeWinding (in); +static void Mod_Q3BSP_LoadVertices(lump_t *l) +{ + q3dvertex_t *in; + int i, count; - return neww; + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name); + loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in); + loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3])); + loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2])); + loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2])); + loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3])); + loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3])); + loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3])); + loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4])); + + for (i = 0;i < count;i++, in++) + { + loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]); + loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]); + loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]); + loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]); + loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]); + loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]); + loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]); + // svector/tvector are calculated later in face loading + loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0; + loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0; + loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0; + loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0; + loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0; + loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0; + loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]); + loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]); + loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]); + loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f); + loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f); + loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f); + loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f); + } } - -/* -================== -DivideWinding - -Divides a winding by a plane, producing one or two windings. The -original winding is not damaged or freed. If only on one side, the -returned winding will be the input winding. If on both sides, two -new windings will be created. -================== -*/ -static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back) +static void Mod_Q3BSP_LoadTriangles(lump_t *l) { - double dists[MAX_POINTS_ON_WINDING + 1]; - int sides[MAX_POINTS_ON_WINDING + 1]; - int counts[3]; - double dot; - int i, j; - double *p1, *p2; - double mid[3]; - winding_t *f, *b; - int maxpts; + int *in; + int *out; + int i, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(int[3])) + Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0; + loadmodel->brushq3.num_triangles = count / 3; + loadmodel->brushq3.data_element3i = out; + loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - // determine sides for each point - for (i = 0;i < in->numpoints;i++) + for (i = 0;i < count;i++, in++, out++) { - dot = DotProduct (in->points[i], split->normal); - dot -= split->dist; - dists[i] = dot; - if (dot > ON_EPSILON) sides[i] = SIDE_FRONT; - else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK; - else sides[i] = SIDE_ON; - counts[sides[i]]++; + *out = LittleLong(*in); + if (*out < 0 || *out >= loadmodel->brushq3.num_vertices) + { + Con_Printf("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices), setting to 0\n", *out, loadmodel->brushq3.num_vertices); + *out = 0; + } } - sides[i] = sides[0]; - dists[i] = dists[0]; +} - *front = *back = NULL; +static void Mod_Q3BSP_LoadLightmaps(lump_t *l) +{ + q3dlightmap_t *in; + rtexture_t **out; + int i, count; - if (!counts[0]) - { - *back = in; - return; - } - if (!counts[1]) - { - *front = in; - return; - } + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + + loadmodel->brushq3.data_lightmaps = out; + loadmodel->brushq3.num_lightmaps = count; + + for (i = 0;i < count;i++, in++, out++) + *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL); +} - maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors +static void Mod_Q3BSP_LoadFaces(lump_t *l) +{ + q3dface_t *in; + q3mface_t *out; + int i, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xlevel, ylevel, row0, row1, x, y, *e, finalvertices, finaltriangles; + //int *originalelement3i; + //int *originalneighbor3i; + float *originalvertex3f; + //float *originalsvector3f; + //float *originaltvector3f; + //float *originalnormal3f; + float *originalcolor4f; + float *originaltexcoordtexture2f; + float *originaltexcoordlightmap2f; + float *v; - if (maxpts > MAX_POINTS_ON_WINDING) - Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING"); + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - *front = f = NewWinding (maxpts); - *back = b = NewWinding (maxpts); + loadmodel->brushq3.data_faces = out; + loadmodel->brushq3.num_faces = count; - for (i = 0;i < in->numpoints;i++) + for (i = 0;i < count;i++, in++, out++) { - if (f->numpoints >= maxpts || b->numpoints >= maxpts) - Sys_Error ("DivideWinding: points exceeded estimate"); + // check face type first + out->type = LittleLong(in->type); + if (out->type != Q3FACETYPE_POLYGON + && out->type != Q3FACETYPE_PATCH + && out->type != Q3FACETYPE_MESH + && out->type != Q3FACETYPE_FLARE) + { + Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type); + out->type = 0; // error + continue; + } - p1 = in->points[i]; + n = LittleLong(in->textureindex); + if (n < 0 || n >= loadmodel->brushq3.num_textures) + { + Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures); + out->type = 0; // error + continue; + n = 0; + } + out->texture = loadmodel->brushq3.data_textures + n; + n = LittleLong(in->effectindex); + if (n < -1 || n >= loadmodel->brushq3.num_effects) + { + Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects); + n = -1; + } + if (n == -1) + out->effect = NULL; + else + out->effect = loadmodel->brushq3.data_effects + n; + n = LittleLong(in->lightmapindex); + if (n < -1 || n >= loadmodel->brushq3.num_lightmaps) + { + Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps); + n = -1; + } + if (n == -1) + out->lightmaptexture = NULL; + else + out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n]; - if (sides[i] == SIDE_ON) + out->firstvertex = LittleLong(in->firstvertex); + out->num_vertices = LittleLong(in->numvertices); + out->firstelement = LittleLong(in->firstelement); + out->num_triangles = LittleLong(in->numelements) / 3; + if (out->num_triangles * 3 != LittleLong(in->numelements)) + { + 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)); + out->type = 0; // error + continue; + } + if (out->firstvertex < 0 || out->firstvertex + out->num_vertices > loadmodel->brushq3.num_vertices) + { + 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); + out->type = 0; // error + continue; + } + if (out->firstelement < 0 || out->firstelement + out->num_triangles * 3 > loadmodel->brushq3.num_triangles * 3) { - VectorCopy (p1, f->points[f->numpoints]); - f->numpoints++; - VectorCopy (p1, b->points[b->numpoints]); - b->numpoints++; + 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); + out->type = 0; // error continue; } - - if (sides[i] == SIDE_FRONT) + out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3; + out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2; + out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2; + out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3; + out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3; + out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3; + out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4; + out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement; + out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement; + switch(out->type) + { + case Q3FACETYPE_POLYGON: + case Q3FACETYPE_MESH: + // no processing necessary + break; + case Q3FACETYPE_PATCH: + patchsize[0] = LittleLong(in->specific.patch.patchsize[0]); + patchsize[1] = LittleLong(in->specific.patch.patchsize[1]); + if (patchsize[0] < 1 || patchsize[1] < 1) + { + Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]); + out->type = 0; // error + continue; + } + // convert patch to Q3FACETYPE_MESH + xlevel = mod_q3bsp_curves_subdivide_level.integer; + ylevel = mod_q3bsp_curves_subdivide_level.integer; + finalwidth = ((patchsize[0] - 1) << xlevel) + 1; + finalheight = ((patchsize[1] - 1) << ylevel) + 1; + finalvertices = finalwidth * finalheight; + finaltriangles = (finalwidth - 1) * (finalheight - 1) * 2; + originalvertex3f = out->data_vertex3f; + //originalsvector3f = out->data_svector3f; + //originaltvector3f = out->data_tvector3f; + //originalnormal3f = out->data_normal3f; + originalcolor4f = out->data_color4f; + originaltexcoordtexture2f = out->data_texcoordtexture2f; + originaltexcoordlightmap2f = out->data_texcoordlightmap2f; + //originalelement3i = out->data_element3i; + //originalneighbor3i = out->data_neighbor3i; + out->data_vertex3f = Mem_Alloc(loadmodel->mempool, sizeof(float[20]) * finalvertices + sizeof(int[6]) * finaltriangles); + out->data_svector3f = out->data_vertex3f + finalvertices * 3; + out->data_tvector3f = out->data_svector3f + finalvertices * 3; + out->data_normal3f = out->data_tvector3f + finalvertices * 3; + out->data_color4f = out->data_normal3f + finalvertices * 3; + out->data_texcoordtexture2f = out->data_color4f + finalvertices * 4; + out->data_texcoordlightmap2f = out->data_texcoordtexture2f + finalvertices * 2; + out->data_element3i = (int *)(out->data_texcoordlightmap2f + finalvertices * 2); + out->data_neighbor3i = out->data_element3i + finaltriangles * 3; + out->type = Q3FACETYPE_MESH; + out->firstvertex = -1; + out->num_vertices = finalvertices; + out->firstelement = -1; + out->num_triangles = finaltriangles; + // generate geometry + // (note: normals are skipped because they get recalculated) + QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 3, originalvertex3f, out->data_vertex3f); + QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordtexture2f, out->data_texcoordtexture2f); + QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 2, originaltexcoordlightmap2f, out->data_texcoordlightmap2f); + QuadraticSplinePatchSubdivideFloatBuffer(patchsize[0], patchsize[1], xlevel, ylevel, 4, originalcolor4f, out->data_color4f); + // generate elements + e = out->data_element3i; + for (y = 0;y < finalheight - 1;y++) + { + row0 = (y + 0) * finalwidth; + row1 = (y + 1) * finalwidth; + for (x = 0;x < finalwidth - 1;x++) + { + *e++ = row0; + *e++ = row1; + *e++ = row0 + 1; + *e++ = row1; + *e++ = row1 + 1; + *e++ = row0 + 1; + row0++; + row1++; + } + } + out->num_triangles = Mod_RemoveDegenerateTriangles(out->num_triangles, out->data_element3i, out->data_element3i, out->data_vertex3f); + if (developer.integer) + { + if (out->num_triangles < finaltriangles) + 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); + else + 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); + } + // q3map does not put in collision brushes for curves... ugh + out->collisions = true; + break; + case Q3FACETYPE_FLARE: + Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name); + // don't render it + out->num_triangles = 0; + break; + } + for (j = 0, invalidelements = 0;j < out->num_triangles * 3;j++) + if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices) + invalidelements++; + if (invalidelements) { - VectorCopy (p1, f->points[f->numpoints]); - f->numpoints++; + 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); + for (j = 0;j < out->num_triangles * 3;j++) + { + Con_Printf(" %i", out->data_element3i[j]); + if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->num_vertices) + out->data_element3i[j] = 0; + } + Con_Printf("\n"); } - else if (sides[i] == SIDE_BACK) + // for shadow volumes + Mod_BuildTriangleNeighbors(out->data_neighbor3i, out->data_element3i, out->num_triangles); + // for per pixel lighting + 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); + // calculate a bounding box + VectorClear(out->mins); + VectorClear(out->maxs); + if (out->num_vertices) { - VectorCopy (p1, b->points[b->numpoints]); - b->numpoints++; + VectorCopy(out->data_vertex3f, out->mins); + VectorCopy(out->data_vertex3f, out->maxs); + for (j = 1, v = out->data_vertex3f + 3;j < out->num_vertices;j++, v += 3) + { + out->mins[0] = min(out->mins[0], v[0]); + out->maxs[0] = max(out->maxs[0], v[0]); + out->mins[1] = min(out->mins[1], v[1]); + out->maxs[1] = max(out->maxs[1], v[1]); + out->mins[2] = min(out->mins[2], v[2]); + out->maxs[2] = max(out->maxs[2], v[2]); + } + out->mins[0] -= 1.0f; + out->mins[1] -= 1.0f; + out->mins[2] -= 1.0f; + out->maxs[0] += 1.0f; + out->maxs[1] += 1.0f; + out->maxs[2] += 1.0f; } + } +} - if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) - continue; +static void Mod_Q3BSP_LoadModels(lump_t *l) +{ + q3dmodel_t *in; + q3mmodel_t *out; + int i, j, n, c, count; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - // generate a split point - p2 = in->points[(i+1)%in->numpoints]; + loadmodel->brushq3.data_models = out; + loadmodel->brushq3.num_models = count; - dot = dists[i] / (dists[i]-dists[i+1]); + for (i = 0;i < count;i++, in++, out++) + { for (j = 0;j < 3;j++) - { // avoid round off error when possible - if (split->normal[j] == 1) - mid[j] = split->dist; - else if (split->normal[j] == -1) - mid[j] = -split->dist; - else - mid[j] = p1[j] + dot*(p2[j]-p1[j]); + { + out->mins[j] = LittleFloat(in->mins[j]); + out->maxs[j] = LittleFloat(in->maxs[j]); } - - VectorCopy (mid, f->points[f->numpoints]); - f->numpoints++; - VectorCopy (mid, b->points[b->numpoints]); - b->numpoints++; + n = LittleLong(in->firstface); + c = LittleLong(in->numfaces); + if (n < 0 || n + c > loadmodel->brushq3.num_faces) + Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces); + out->firstface = loadmodel->brushq3.data_faces + n; + out->numfaces = c; + n = LittleLong(in->firstbrush); + c = LittleLong(in->numbrushes); + if (n < 0 || n + c > loadmodel->brushq3.num_brushes) + Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes); + out->firstbrush = loadmodel->brushq3.data_brushes + n; + out->numbrushes = c; } } -typedef struct portal_s +static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l) { - mplane_t plane; - mnode_t *nodes[2]; // [0] = front side of plane - struct portal_s *next[2]; - winding_t *winding; - struct portal_s *chain; // all portals are linked into a list -} -portal_t; + int *in; + q3mbrush_t **out; + int i, n, count; -static portal_t *portalchain; + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); -/* -=========== -AllocPortal -=========== -*/ -static portal_t *AllocPortal (void) -{ - portal_t *p; - p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t)); - p->chain = portalchain; - portalchain = p; - return p; -} + loadmodel->brushq3.data_leafbrushes = out; + loadmodel->brushq3.num_leafbrushes = count; -static void FreePortal(portal_t *p) -{ - Mem_Free(p); + for (i = 0;i < count;i++, in++, out++) + { + n = LittleLong(*in); + if (n < 0 || n >= loadmodel->brushq3.num_brushes) + Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes); + *out = loadmodel->brushq3.data_brushes + n; + } } -static void Mod_RecursiveRecalcNodeBBox(mnode_t *node) +static void Mod_Q3BSP_LoadLeafFaces(lump_t *l) { - // calculate children first - if (node->children[0]->contents >= 0) - Mod_RecursiveRecalcNodeBBox(node->children[0]); - if (node->children[1]->contents >= 0) - Mod_RecursiveRecalcNodeBBox(node->children[1]); + int *in; + q3mface_t **out; + int i, n, count; - // make combined bounding box from children - node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]); - node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]); - node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]); - node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]); - node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]); - node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]); -} + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); -static void Mod_FinalizePortals(void) -{ - int i, j, numportals, numpoints; - portal_t *p, *pnext; - mportal_t *portal; - mvertex_t *point; - mleaf_t *leaf, *endleaf; - winding_t *w; + loadmodel->brushq3.data_leaffaces = out; + loadmodel->brushq3.num_leaffaces = count; - // recalculate bounding boxes for all leafs (because qbsp is very sloppy) - leaf = loadmodel->leafs; - endleaf = leaf + loadmodel->numleafs; - for (;leaf < endleaf;leaf++) - { - VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000); - VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000); - } - p = portalchain; - while(p) + for (i = 0;i < count;i++, in++, out++) { - if (p->winding) - { - for (i = 0;i < 2;i++) - { - leaf = (mleaf_t *)p->nodes[i]; - w = p->winding; - for (j = 0;j < w->numpoints;j++) - { - if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0]; - if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1]; - if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2]; - if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0]; - if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1]; - if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2]; - } - } - } - p = p->chain; + n = LittleLong(*in); + if (n < 0 || n >= loadmodel->brushq3.num_faces) + Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces); + *out = loadmodel->brushq3.data_faces + n; } +} - Mod_RecursiveRecalcNodeBBox(loadmodel->nodes); +static void Mod_Q3BSP_LoadLeafs(lump_t *l) +{ + q3dleaf_t *in; + q3mleaf_t *out; + int i, j, n, c, count; - // tally up portal and point counts - p = portalchain; - numportals = 0; - numpoints = 0; - while(p) + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + + loadmodel->brushq3.data_leafs = out; + loadmodel->brushq3.num_leafs = count; + + for (i = 0;i < count;i++, in++, out++) { - // note: this check must match the one below or it will usually corrupt memory - // 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 - if (p->winding && p->nodes[0] != p->nodes[1] - && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID - && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY) + out->isnode = false; + out->parent = NULL; + out->clusterindex = LittleLong(in->clusterindex); + out->areaindex = LittleLong(in->areaindex); + for (j = 0;j < 3;j++) { - numportals += 2; - numpoints += p->winding->numpoints * 2; + // yes the mins/maxs are ints + out->mins[j] = LittleLong(in->mins[j]); + out->maxs[j] = LittleLong(in->maxs[j]); } - p = p->chain; + n = LittleLong(in->firstleafface); + c = LittleLong(in->numleaffaces); + if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces) + Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces); + out->firstleafface = loadmodel->brushq3.data_leaffaces + n; + out->numleaffaces = c; + n = LittleLong(in->firstleafbrush); + c = LittleLong(in->numleafbrushes); + if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes) + Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes); + out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n; + out->numleafbrushes = c; } - loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t)); - loadmodel->numportals = numportals; - loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t)); - loadmodel->numportalpoints = numpoints; - // clear all leaf portal chains - for (i = 0;i < loadmodel->numleafs;i++) - loadmodel->leafs[i].portals = NULL; - // process all portals in the global portal chain, while freeing them - portal = loadmodel->portals; - point = loadmodel->portalpoints; - p = portalchain; - portalchain = NULL; - while (p) - { - pnext = p->chain; - - if (p->winding) - { - // note: this check must match the one above or it will usually corrupt memory - // 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 - if (p->nodes[0] != p->nodes[1] - && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID - && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY) - { - // first make the back to front portal (forward portal) - portal->points = point; - portal->numpoints = p->winding->numpoints; - portal->plane.dist = p->plane.dist; - VectorCopy(p->plane.normal, portal->plane.normal); - portal->here = (mleaf_t *)p->nodes[1]; - portal->past = (mleaf_t *)p->nodes[0]; - // copy points - for (j = 0;j < portal->numpoints;j++) - { - VectorCopy(p->winding->points[j], point->position); - point++; - } - PlaneClassify(&portal->plane); - - // link into leaf's portal chain - portal->next = portal->here->portals; - portal->here->portals = portal; - - // advance to next portal - portal++; - - // then make the front to back portal (backward portal) - portal->points = point; - portal->numpoints = p->winding->numpoints; - portal->plane.dist = -p->plane.dist; - VectorNegate(p->plane.normal, portal->plane.normal); - portal->here = (mleaf_t *)p->nodes[0]; - portal->past = (mleaf_t *)p->nodes[1]; - // copy points - for (j = portal->numpoints - 1;j >= 0;j--) - { - VectorCopy(p->winding->points[j], point->position); - point++; - } - PlaneClassify(&portal->plane); - - // link into leaf's portal chain - portal->next = portal->here->portals; - portal->here->portals = portal; +} - // advance to next portal - portal++; - } - FreeWinding(p->winding); - } - FreePortal(p); - p = pnext; +static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent) +{ + if (node->parent) + Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n"); + node->parent = parent; + if (node->isnode) + { + Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node); + Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node); } } -/* -============= -AddPortalToNodes -============= -*/ -static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back) +static void Mod_Q3BSP_LoadNodes(lump_t *l) { - if (!front) - Host_Error ("AddPortalToNodes: NULL front node"); - if (!back) - Host_Error ("AddPortalToNodes: NULL back node"); - if (p->nodes[0] || p->nodes[1]) - Host_Error ("AddPortalToNodes: already included"); - // 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 + q3dnode_t *in; + q3mnode_t *out; + int i, j, n, count; - p->nodes[0] = front; - p->next[0] = (portal_t *)front->portals; - front->portals = (mportal_t *)p; + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name); + count = l->filelen / sizeof(*in); + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - p->nodes[1] = back; - p->next[1] = (portal_t *)back->portals; - back->portals = (mportal_t *)p; -} + loadmodel->brushq3.data_nodes = out; + loadmodel->brushq3.num_nodes = count; -/* -============= -RemovePortalFromNode -============= -*/ -static void RemovePortalFromNodes(portal_t *portal) -{ - int i; - mnode_t *node; - void **portalpointer; - portal_t *t; - for (i = 0;i < 2;i++) + for (i = 0;i < count;i++, in++, out++) { - node = portal->nodes[i]; - - portalpointer = (void **) &node->portals; - while (1) + out->isnode = true; + out->parent = NULL; + n = LittleLong(in->planeindex); + if (n < 0 || n >= loadmodel->brushq3.num_planes) + Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes); + out->plane = loadmodel->brushq3.data_planes + n; + for (j = 0;j < 2;j++) { - t = *portalpointer; - if (!t) - Host_Error ("RemovePortalFromNodes: portal not in leaf"); - - if (t == portal) + n = LittleLong(in->childrenindex[j]); + if (n >= 0) { - if (portal->nodes[0] == node) - { - *portalpointer = portal->next[0]; - portal->nodes[0] = NULL; - } - else if (portal->nodes[1] == node) - { - *portalpointer = portal->next[1]; - portal->nodes[1] = NULL; - } - else - Host_Error ("RemovePortalFromNodes: portal not bounding leaf"); - break; + if (n >= loadmodel->brushq3.num_nodes) + Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes); + out->children[j] = loadmodel->brushq3.data_nodes + n; } - - if (t->nodes[0] == node) - portalpointer = (void **) &t->next[0]; - else if (t->nodes[1] == node) - portalpointer = (void **) &t->next[1]; else - Host_Error ("RemovePortalFromNodes: portal not bounding leaf"); + { + n = -1 - n; + if (n >= loadmodel->brushq3.num_leafs) + Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs); + out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n); + } + } + for (j = 0;j < 3;j++) + { + // yes the mins/maxs are ints + out->mins[j] = LittleLong(in->mins[j]); + out->maxs[j] = LittleLong(in->maxs[j]); } } + + // set the parent pointers + Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL); +} + +static void Mod_Q3BSP_LoadLightGrid(lump_t *l) +{ + q3dlightgrid_t *in; + q3dlightgrid_t *out; + int count; + + if (l->filelen == 0) + return; + + in = (void *)(mod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name); + loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0]; + loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1]; + loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2]; + loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]); + loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]); + loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]); + loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]); + loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]); + loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]); + loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1; + loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1; + loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1; + count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2]; + if (l->filelen < count * (int)sizeof(*in)) + 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]); + if (l->filelen != count * (int)sizeof(*in)) + Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen); + + out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + loadmodel->brushq3.data_lightgrid = out; + loadmodel->brushq3.num_lightgrid = count; + + // no swapping or validation necessary + memcpy(out, in, count * (int)sizeof(*out)); + + 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]); + 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]); } -static void Mod_RecursiveNodePortals (mnode_t *node) +static void Mod_Q3BSP_LoadPVS(lump_t *l) { - int side; - mnode_t *front, *back, *other_node; - mplane_t clipplane, *plane; - portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp; - winding_t *nodeportalwinding, *frontwinding, *backwinding; + q3dpvs_t *in; + int totalchains; - // if a leaf, we're done - if (node->contents) + if (l->filelen == 0) return; - plane = node->plane; + in = (void *)(mod_base + l->fileofs); + if (l->filelen < 9) + Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name); + + loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters); + loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength); + if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8)) + Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters); + totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters; + if (l->filelen < totalchains + (int)sizeof(*in)) + 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); + + loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains); + memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains); +} - front = node->children[0]; - back = node->children[1]; - if (front == back) - Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy"); +static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius) +{ + // FIXME: finish this code + VectorCopy(in, out); +} - // create the new portal by generating a polygon for the node plane, - // and clipping it by all of the other portals (which came from nodes above this one) - nodeportal = AllocPortal (); - nodeportal->plane = *node->plane; +static void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal) +{ + int i, j, k, index[3]; + float transformed[3], blend1, blend2, blend, yaw, pitch, sinpitch; + q3dlightgrid_t *a, *s; + // FIXME: write this + if (!model->brushq3.num_lightgrid) + { + ambientcolor[0] += 128; + ambientcolor[1] += 128; + ambientcolor[2] += 128; + return; + } + Matrix4x4_Transform(&model->brushq3.num_lightgrid_indexfromworld, p, transformed); + //Matrix4x4_Print(&model->brushq3.num_lightgrid_indexfromworld); + //Con_Printf("%f %f %f transformed %f %f %f clamped ", p[0], p[1], p[2], transformed[0], transformed[1], transformed[2]); + transformed[0] = bound(0, transformed[0], model->brushq3.num_lightgrid_isize[0] - 1); + transformed[1] = bound(0, transformed[1], model->brushq3.num_lightgrid_isize[1] - 1); + transformed[2] = bound(0, transformed[2], model->brushq3.num_lightgrid_isize[2] - 1); + index[0] = (int)floor(transformed[0]); + index[1] = (int)floor(transformed[1]); + index[2] = (int)floor(transformed[2]); + //Con_Printf("%f %f %f index %i %i %i:\n", transformed[0], transformed[1], transformed[2], index[0], index[1], index[2]); + // now lerp the values + VectorClear(diffusenormal); + a = &model->brushq3.data_lightgrid[(index[2] * model->brushq3.num_lightgrid_isize[1] + index[1]) * model->brushq3.num_lightgrid_isize[0] + index[0]]; + for (k = 0;k < 2;k++) + { + blend1 = (k ? (transformed[2] - index[2]) : (1 - (transformed[2] - index[2]))); + if (blend1 < 0.001f || index[2] + k >= model->brushq3.num_lightgrid_isize[2]) + continue; + for (j = 0;j < 2;j++) + { + blend2 = blend1 * (j ? (transformed[1] - index[1]) : (1 - (transformed[1] - index[1]))); + if (blend2 < 0.001f || index[1] + j >= model->brushq3.num_lightgrid_isize[1]) + continue; + for (i = 0;i < 2;i++) + { + blend = blend2 * (i ? (transformed[0] - index[0]) : (1 - (transformed[0] - index[0]))); + if (blend < 0.001f || index[0] + i >= model->brushq3.num_lightgrid_isize[0]) + continue; + s = a + (k * model->brushq3.num_lightgrid_isize[1] + j) * model->brushq3.num_lightgrid_isize[0] + i; + VectorMA(ambientcolor, blend * (1.0f / 128.0f), s->ambientrgb, ambientcolor); + VectorMA(diffusecolor, blend * (1.0f / 128.0f), s->diffusergb, diffusecolor); + pitch = s->diffusepitch * M_PI / 128; + yaw = s->diffuseyaw * M_PI / 128; + sinpitch = sin(pitch); + diffusenormal[0] += blend * (cos(yaw) * sinpitch); + diffusenormal[1] += blend * (sin(yaw) * sinpitch); + diffusenormal[2] += blend * (cos(pitch)); + //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))); + } + } + } + VectorNormalize(diffusenormal); + //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]); +} - nodeportalwinding = BaseWindingForPlane (node->plane); - side = 0; // shut up compiler warning - for (portal = (portal_t *)node->portals;portal;portal = portal->next[side]) +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) +{ + int i, startside, endside; + float dist1, dist2, midfrac, mid[3], segmentmins[3], segmentmaxs[3]; + q3mleaf_t *leaf; + q3mface_t *face; + colbrushf_t *brush; + if (startfrac >= trace->fraction) + return; + // note: all line fragments past first impact fraction are ignored + while (node->isnode) { - clipplane = portal->plane; - if (portal->nodes[0] == portal->nodes[1]) - Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)"); - if (portal->nodes[0] == node) - side = 0; - else if (portal->nodes[1] == node) + // recurse down node sides + dist1 = PlaneDiff(start, node->plane); + dist2 = PlaneDiff(end, node->plane); + startside = dist1 < 0; + endside = dist2 < 0; + if (startside == endside) { - clipplane.dist = -clipplane.dist; - VectorNegate (clipplane.normal, clipplane.normal); - side = 1; + // most of the time the line fragment is on one side of the plane + node = node->children[startside]; } else - Host_Error ("Mod_RecursiveNodePortals: mislinked portal"); - - nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true); - if (!nodeportalwinding) { - printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n"); - break; + // line crosses node plane, split the line + midfrac = dist1 / (dist1 - dist2); + VectorLerp(linestart, midfrac, lineend, mid); + // take the near side first + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe); + if (midfrac < trace->fraction) + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe); + return; } } - - if (nodeportalwinding) + // hit a leaf + segmentmins[0] = min(start[0], end[0]); + segmentmins[1] = min(start[1], end[1]); + segmentmins[2] = min(start[2], end[2]); + segmentmaxs[0] = max(start[0], end[0]); + segmentmaxs[1] = max(start[1], end[1]); + segmentmaxs[2] = max(start[2], end[2]); + leaf = (q3mleaf_t *)node; + for (i = 0;i < leaf->numleafbrushes;i++) { - // if the plane was not clipped on all sides, there was an error - nodeportal->winding = nodeportalwinding; - AddPortalToNodes (nodeportal, front, back); + if (startfrac >= trace->fraction) + return; + brush = leaf->firstleafbrush[i]->colbrushf; + if (brush && brush->markframe != markframe) + { + brush->markframe = markframe; + if (BoxesOverlap(segmentmins, segmentmaxs, brush->mins, brush->maxs)) + Collision_TraceLineBrushFloat(trace, linestart, lineend, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf); + } } - - // split the portals of this node along this node's plane and assign them to the children of this node - // (migrating the portals downward through the tree) - for (portal = (portal_t *)node->portals;portal;portal = nextportal) + if (mod_q3bsp_curves_collisions.integer) { - if (portal->nodes[0] == portal->nodes[1]) - Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)"); - if (portal->nodes[0] == node) - side = 0; - else if (portal->nodes[1] == node) - side = 1; - else - Host_Error ("Mod_RecursiveNodePortals: mislinked portal"); - nextportal = portal->next[side]; - - other_node = portal->nodes[!side]; - RemovePortalFromNodes (portal); - - // cut the portal into two portals, one on each side of the node plane - DivideWinding (portal->winding, plane, &frontwinding, &backwinding); + for (i = 0;i < leaf->numleaffaces;i++) + { + if (startfrac >= trace->fraction) + return; + face = leaf->firstleafface[i]; + if (face->collisions && face->collisionmarkframe != markframe) + { + face->collisionmarkframe = markframe; + if (BoxesOverlap(segmentmins, segmentmaxs, face->mins, face->maxs)) + Collision_TraceLineTriangleMeshFloat(trace, linestart, lineend, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } + } +} - if (!frontwinding) +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) +{ + int i, sides; + float nodesegmentmins[3], nodesegmentmaxs[3]; + q3mleaf_t *leaf; + colbrushf_t *brush; + q3mface_t *face; + nodesegmentmins[0] = max(segmentmins[0], node->mins[0]); + nodesegmentmins[1] = max(segmentmins[1], node->mins[1]); + nodesegmentmins[2] = max(segmentmins[2], node->mins[2]); + nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]); + nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]); + nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]); + if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2]) + return; + if (node->isnode) + { + // recurse down node sides + sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane); + if (sides == 3) { - if (side == 0) - AddPortalToNodes (portal, back, other_node); - else - AddPortalToNodes (portal, other_node, back); - continue; + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs); + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs); } - if (!backwinding) + else if (sides == 2) + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + else // sides == 1 + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + /* + dist = node->plane->dist - (1.0f / 8.0f); + for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++) { - if (side == 0) - AddPortalToNodes (portal, front, other_node); - else - AddPortalToNodes (portal, other_node, front); - continue; + if (DotProduct(ps->v, node->plane->normal) >= dist || DotProduct(pe->v, node->plane->normal) >= dist) + { + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs); + break; + } } + */ + /* + dist = node->plane->dist + (1.0f / 8.0f); + for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++) + { + if (DotProduct(ps->v, node->plane->normal) <= dist || DotProduct(pe->v, node->plane->normal) <= dist) + { + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end, markframe, nodesegmentmins, nodesegmentmaxs); + break; + } + } + */ + /* + sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane); + if (sides & 1) + Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs); + if (sides & 2) + Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs); + */ + } + else + { + // hit a leaf + leaf = (q3mleaf_t *)node; + for (i = 0;i < leaf->numleafbrushes;i++) + { + brush = leaf->firstleafbrush[i]->colbrushf; + if (brush && brush->markframe != markframe && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, brush->mins, brush->maxs)) + { + brush->markframe = markframe; + Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf); + } + } + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < leaf->numleaffaces;i++) + { + face = leaf->firstleafface[i]; + // 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 + if (face->collisions && BoxesOverlap(nodesegmentmins, nodesegmentmaxs, face->mins, face->maxs)) + Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } + } +} - // the winding is split - splitportal = AllocPortal (); - temp = splitportal->chain; - *splitportal = *portal; - splitportal->chain = temp; - splitportal->winding = backwinding; - FreeWinding (portal->winding); - portal->winding = frontwinding; - - if (side == 0) +static void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs, int hitsupercontentsmask) +{ + int i; + float segmentmins[3], segmentmaxs[3]; + colbrushf_t *thisbrush_start, *thisbrush_end; + matrix4x4_t startmatrix, endmatrix; + static int markframe = 0; + q3mface_t *face; + memset(trace, 0, sizeof(*trace)); + trace->fraction = 1; + trace->hitsupercontentsmask = hitsupercontentsmask; + Matrix4x4_CreateIdentity(&startmatrix); + Matrix4x4_CreateIdentity(&endmatrix); + segmentmins[0] = min(boxstartmins[0], boxendmins[0]); + segmentmins[1] = min(boxstartmins[1], boxendmins[1]); + segmentmins[2] = min(boxstartmins[2], boxendmins[2]); + segmentmaxs[0] = max(boxstartmaxs[0], boxendmaxs[0]); + segmentmaxs[1] = max(boxstartmaxs[1], boxendmaxs[1]); + segmentmaxs[2] = max(boxstartmaxs[2], boxendmaxs[2]); + if (mod_q3bsp_optimizedtraceline.integer && VectorCompare(boxstartmins, boxstartmaxs) && VectorCompare(boxendmins, boxendmaxs)) + { + // line trace + if (model->brushq3.submodel) { - AddPortalToNodes (portal, front, other_node); - AddPortalToNodes (splitportal, back, other_node); + for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++) + if (model->brushq3.data_thismodel->firstbrush[i].colbrushf) + Collision_TraceLineBrushFloat(trace, boxstartmins, boxendmins, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf); + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++) + { + face = model->brushq3.data_thismodel->firstface + i; + if (face->collisions) + Collision_TraceLineTriangleMeshFloat(trace, boxstartmins, boxendmins, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } } else + Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model->brushq3.data_nodes, boxstartmins, boxendmins, 0, 1, boxstartmins, boxendmins, ++markframe); + } + else + { + // box trace, performed as brush trace + thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs); + thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs); + if (model->brushq3.submodel) { - AddPortalToNodes (portal, other_node, front); - AddPortalToNodes (splitportal, other_node, back); + for (i = 0;i < model->brushq3.data_thismodel->numbrushes;i++) + if (model->brushq3.data_thismodel->firstbrush[i].colbrushf) + Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_thismodel->firstbrush[i].colbrushf, model->brushq3.data_thismodel->firstbrush[i].colbrushf); + if (mod_q3bsp_curves_collisions.integer) + { + for (i = 0;i < model->brushq3.data_thismodel->numfaces;i++) + { + face = model->brushq3.data_thismodel->firstface + i; + if (face->collisions) + Collision_TraceBrushTriangleMeshFloat(trace, thisbrush_start, thisbrush_end, face->num_triangles, face->data_element3i, face->data_vertex3f, face->texture->supercontents, segmentmins, segmentmaxs); + } + } } + else + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end, ++markframe, segmentmins, segmentmaxs); } - - Mod_RecursiveNodePortals(front); - Mod_RecursiveNodePortals(back); } -static void Mod_MakePortals(void) +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) { - portalchain = NULL; - Mod_RecursiveNodePortals (loadmodel->nodes); - Mod_FinalizePortals(); + int clusterindex; +loc0: + if (!node->isnode) + { + // leaf + clusterindex = ((q3mleaf_t *)node)->clusterindex; + return pvs[clusterindex >> 3] & (1 << (clusterindex & 7)); + } + + // node - recurse down the BSP tree + switch (BoxOnPlaneSide(mins, maxs, node->plane)) + { + case 1: // front + node = node->children[0]; + goto loc0; + case 2: // back + node = node->children[1]; + goto loc0; + default: // crossing + if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs)) + return true; + node = node->children[1]; + goto loc0; + } + // never reached + return false; } -/* -================= -Mod_LoadBrushModel -================= -*/ -extern void R_Model_Brush_DrawSky(entity_render_t *ent); -extern void R_Model_Brush_Draw(entity_render_t *ent); -//extern void R_Model_Brush_DrawFakeShadow(entity_render_t *ent); -extern void R_Model_Brush_DrawDepth(entity_render_t *ent); -extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int visiblevolume); -extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor); -extern void R_Model_Brush_DrawOntoLight(entity_render_t *ent); -void Mod_LoadBrushModel (model_t *mod, void *buffer) -{ - int i, j; - dheader_t *header; - dmodel_t *bm; - mempool_t *mainmempool; - char *loadname; - model_t *originalloadmodel; +static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) +{ + return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs); +} - mod->type = mod_brush; +//Returns PVS data for a given point +//(note: can return NULL) +static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p) +{ + q3mnode_t *node; + Mod_CheckLoaded(model); + node = model->brushq3.data_nodes; + while (node->isnode) + node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist]; + if (((q3mleaf_t *)node)->clusterindex >= 0) + return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength; + else + return NULL; +} - header = (dheader_t *)buffer; +static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node) +{ + int i; + float d; + qbyte *pvs; - i = LittleLong (header->version); - if (i != BSPVERSION && i != 30) - Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION); - mod->ishlbsp = i == 30; - if (loadmodel->isworldmodel) + while (node->isnode) { - Cvar_SetValue("halflifebsp", mod->ishlbsp); - // until we get a texture for it... - R_ResetQuakeSky(); + d = PlaneDiff(org, node->plane); + if (d > radius) + node = node->children[0]; + else if (d < -radius) + node = node->children[1]; + else + { + // go down both sides + Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]); + node = node->children[1]; + } } + // if this is a leaf with a pvs, accumulate the pvs bits + if (((q3mleaf_t *)node)->clusterindex >= 0) + { + pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength; + for (i = 0;i < pvsbytes;i++) + pvsbuffer[i] |= pvs[i]; + } + return; +} -// swap all the lumps - mod_base = (qbyte *)header; +//Calculates a PVS that is the inclusive or of all leafs within radius pixels +//of the given point. +static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength) +{ + int bytes = model->brushq3.num_pvschainlength; + bytes = min(bytes, pvsbufferlength); + memset(pvsbuffer, 0, bytes); + Mod_Q3BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brushq3.data_nodes); + return bytes; +} - for (i=0 ; ilightmaprgba = r_lightmaprgba.integer; - - Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); - Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]); - Mod_LoadEdges (&header->lumps[LUMP_EDGES]); - Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); - Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); - Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]); - Mod_LoadPlanes (&header->lumps[LUMP_PLANES]); - Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]); - Mod_LoadFaces (&header->lumps[LUMP_FACES]); - Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]); - Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); - Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); - Mod_LoadNodes (&header->lumps[LUMP_NODES]); - Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); - Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); - - Mod_MakeHull0 (); - Mod_MakePortals(); +static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int supercontents) +{ + int nativecontents = 0; + if (supercontents & SUPERCONTENTS_SOLID) + nativecontents |= Q2CONTENTS_SOLID; + if (supercontents & SUPERCONTENTS_WATER) + nativecontents |= Q2CONTENTS_WATER; + if (supercontents & SUPERCONTENTS_SLIME) + nativecontents |= Q2CONTENTS_SLIME; + if (supercontents & SUPERCONTENTS_LAVA) + nativecontents |= Q2CONTENTS_LAVA; + return nativecontents; +} - mod->numframes = 2; // regular and alternate animation +//extern void R_Q3BSP_DrawSky(struct entity_render_s *ent); +extern void R_Q3BSP_Draw(struct entity_render_s *ent); +extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius); +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); +void Mod_Q3BSP_Load(model_t *mod, void *buffer) +{ + int i; + q3dheader_t *header; + float corner[3], yawradius, modelradius; - mainmempool = mod->mempool; - loadname = mod->name; + mod->type = mod_brushq3; + mod->numframes = 1; + mod->numskins = 1; - Mod_LoadLightList (); - originalloadmodel = loadmodel; + header = (q3dheader_t *)buffer; -// -// set up the submodels (FIXME: this is confusing) -// - for (i = 0;i < mod->numsubmodels;i++) + i = LittleLong(header->version); + if (i != Q3BSPVERSION) + Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION); + if (loadmodel->isworldmodel) { - int k, l; - float dist, modelyawradius, modelradius, *vec; - msurface_t *surf; - - mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f; - mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f; - modelyawradius = 0; - modelradius = 0; - - bm = &mod->submodels[i]; + Cvar_SetValue("halflifebsp", false); + // until we get a texture for it... + R_ResetQuakeSky(); + } - mod->hulls[0].firstclipnode = bm->headnode[0]; - for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; - mod->hulls[j].lastclipnode = mod->numclipnodes - 1; - } + mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents; + mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents; + mod->brush.GetPVS = Mod_Q3BSP_GetPVS; + mod->brush.FatPVS = Mod_Q3BSP_FatPVS; + mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS; + mod->brush.LightPoint = Mod_Q3BSP_LightPoint; + mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation; + mod->brush.TraceBox = Mod_Q3BSP_TraceBox; + //mod->DrawSky = R_Q3BSP_DrawSky; + mod->Draw = R_Q3BSP_Draw; + mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume; + mod->DrawLight = R_Q3BSP_DrawLight; - mod->firstmodelsurface = bm->firstface; - mod->nummodelsurfaces = bm->numfaces; + mod_base = (qbyte *)header; - // this gets altered below if sky is used - mod->DrawSky = NULL; - mod->Draw = R_Model_Brush_Draw; - mod->DrawFakeShadow = NULL; - mod->DrawDepth = R_Model_Brush_DrawDepth; - mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume; - mod->DrawLight = R_Model_Brush_DrawLight; - mod->DrawOntoLight = R_Model_Brush_DrawOntoLight; - if (mod->nummodelsurfaces) - { - // LordHavoc: calculate bmodel bounding box rather than trusting what it says - for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++) - { - // we only need to have a drawsky function if it is used (usually only on world model) - if (surf->texinfo->texture->shader == &Cshader_sky) - mod->DrawSky = R_Model_Brush_DrawSky; - for (k = 0;k < surf->numedges;k++) - { - l = mod->surfedges[k + surf->firstedge]; - if (l > 0) - vec = mod->vertexes[mod->edges[l].v[0]].position; - else - vec = mod->vertexes[mod->edges[-l].v[1]].position; - if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0]; - if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1]; - if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2]; - if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0]; - if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1]; - if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2]; - dist = vec[0]*vec[0]+vec[1]*vec[1]; - if (modelyawradius < dist) - modelyawradius = dist; - dist += vec[2]*vec[2]; - if (modelradius < dist) - modelradius = dist; - } - } - modelyawradius = sqrt(modelyawradius); - modelradius = sqrt(modelradius); - mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius); - mod->yawmins[2] = mod->normalmins[2]; - mod->yawmaxs[2] = mod->normalmaxs[2]; - mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius; - mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius; - mod->radius = modelradius; - mod->radius2 = modelradius * modelradius; - // LordHavoc: build triangle meshs for entire model's geometry - // (only used for shadow volumes) - mod->shadowmesh = Mod_ShadowMesh_Begin(originalloadmodel->mempool); - for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++) - if (surf->flags & SURF_CLIPSOLID) - Mod_ShadowMesh_AddPolygon(originalloadmodel->mempool, mod->shadowmesh, surf->poly_numverts, surf->poly_verts); - mod->shadowmesh = Mod_ShadowMesh_Finish(originalloadmodel->mempool, mod->shadowmesh); - Mod_ShadowMesh_CalcBBox(mod->shadowmesh, mod->shadowmesh_mins, mod->shadowmesh_maxs, mod->shadowmesh_center, &mod->shadowmesh_radius); - } + // swap all the lumps + for (i = 0;i < (int) sizeof(*header) / 4;i++) + ((int *)header)[i] = LittleLong(((int *)header)[i]); + + Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]); + Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]); + Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]); + Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]); + Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]); + Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]); + Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]); + Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]); + Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]); + Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]); + Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]); + Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]); + Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]); + Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]); + Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]); + Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]); + Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]); + loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models; + + for (i = 0;i < loadmodel->brushq3.num_models;i++) + { + if (i == 0) + mod = loadmodel; else { - // LordHavoc: empty submodel (lacrima.bsp has such a glitch) - Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname); - VectorClear(mod->normalmins); - VectorClear(mod->normalmaxs); - VectorClear(mod->yawmins); - VectorClear(mod->yawmaxs); - VectorClear(mod->rotatedmins); - VectorClear(mod->rotatedmaxs); - mod->radius = 0; - mod->radius2 = 0; - mod->shadowmesh = NULL; - } - - mod->numleafs = bm->visleafs; - - // LordHavoc: only register submodels if it is the world - // (prevents bsp models from replacing world submodels) - if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1)) - { - char name[10]; + char name[10]; + // LordHavoc: only register submodels if it is the world + // (prevents bsp models from replacing world submodels) + if (!loadmodel->isworldmodel) + continue; // duplicate the basic information - sprintf (name, "*%i", i+1); - loadmodel = Mod_FindName (name); - *loadmodel = *mod; - strcpy (loadmodel->name, name); + sprintf(name, "*%i", i); + mod = Mod_FindName(name); + *mod = *loadmodel; + strcpy(mod->name, name); // textures and memory belong to the main model - loadmodel->texturepool = NULL; - loadmodel->mempool = NULL; - mod = loadmodel; + mod->texturepool = NULL; + mod->mempool = NULL; } + mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i; + mod->brushq3.submodel = i; + + VectorCopy(mod->brushq3.data_thismodel->mins, mod->normalmins); + VectorCopy(mod->brushq3.data_thismodel->maxs, mod->normalmaxs); + corner[0] = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0])); + corner[1] = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1])); + corner[2] = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2])); + modelradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]+corner[2]*corner[2]); + yawradius = sqrt(corner[0]*corner[0]+corner[1]*corner[1]); + mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius; + mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius; + mod->yawmaxs[0] = mod->yawmaxs[1] = yawradius; + mod->yawmins[0] = mod->yawmins[1] = -yawradius; + mod->yawmins[2] = mod->normalmins[2]; + mod->yawmaxs[2] = mod->normalmaxs[2]; + mod->radius = modelradius; + mod->radius2 = modelradius * modelradius; } +} - loadmodel = originalloadmodel; - Mod_ProcessLightList (); +void Mod_IBSP_Load(model_t *mod, void *buffer) +{ + int i = LittleLong(((int *)buffer)[1]); + if (i == Q3BSPVERSION) + Mod_Q3BSP_Load(mod,buffer); + else if (i == Q2BSPVERSION) + Mod_Q2BSP_Load(mod,buffer); + else + Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i); +} + +void Mod_MAP_Load(model_t *mod, void *buffer) +{ + Host_Error("Mod_MAP_Load: not yet implemented\n"); }