X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=model_brush.c;h=5cc71a7eacd80fd79bb8c4d0136948027b1aefcf;hb=83abc8e6498d3a7a97678660498bf3607a4aa6a5;hp=b8e64da458f37cbd8582ee13b863ee3013112289;hpb=d67837d6d6ba53a1cc1c20cc8fad1bf96a4cdba2;p=xonotic%2Fdarkplaces.git diff --git a/model_brush.c b/model_brush.c index b8e64da4..5cc71a7e 100644 --- a/model_brush.c +++ b/model_brush.c @@ -39,6 +39,7 @@ cvar_t r_subdivisions_collision_mintess = {0, "r_subdivisions_collision_mintess" cvar_t r_subdivisions_collision_maxtess = {0, "r_subdivisions_collision_maxtess", "1024", "maximum number of subdivisions (prevents curves beyond a certain detail level, limits smoothing)"}; cvar_t r_subdivisions_collision_maxvertices = {0, "r_subdivisions_collision_maxvertices", "4225", "maximum vertices allowed per subdivided curve"}; cvar_t r_trippy = {0, "r_trippy", "0", "easter egg"}; +cvar_t r_fxaa = {CVAR_SAVE, "r_fxaa", "0", "fast approximate anti aliasing"}; cvar_t mod_noshader_default_offsetmapping = {CVAR_SAVE, "mod_noshader_default_offsetmapping", "1", "use offsetmapping by default on all surfaces that are not using q3 shader files"}; cvar_t mod_obj_orientation = {0, "mod_obj_orientation", "1", "fix orientation of OBJ models to the usual conventions (if zero, use coordinates as is)"}; cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"}; @@ -68,6 +69,8 @@ static texture_t mod_q1bsp_texture_lava; static texture_t mod_q1bsp_texture_slime; static texture_t mod_q1bsp_texture_water; +static qboolean Mod_Q3BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end); + void Mod_BrushInit(void) { // Cvar_RegisterVariable(&r_subdivide_size); @@ -83,6 +86,7 @@ void Mod_BrushInit(void) Cvar_RegisterVariable(&r_subdivisions_collision_maxtess); Cvar_RegisterVariable(&r_subdivisions_collision_maxvertices); Cvar_RegisterVariable(&r_trippy); + Cvar_RegisterVariable(&r_fxaa); Cvar_RegisterVariable(&mod_noshader_default_offsetmapping); Cvar_RegisterVariable(&mod_obj_orientation); Cvar_RegisterVariable(&mod_q3bsp_curves_collisions); @@ -691,7 +695,6 @@ RecursiveHullCheckTraceInfo_t; #define HULLCHECKSTATE_SOLID 1 #define HULLCHECKSTATE_DONE 2 -extern cvar_t collision_prefernudgedfraction; 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 @@ -703,156 +706,151 @@ static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int nu // variables that need to be stored on the stack when recursing mclipnode_t *node; - int side; + int p1side, p2side; 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) - { - 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 & SUPERCONTENTS_SOLID) - t->trace->hittexture = &mod_q1bsp_texture_solid; - else if (num & SUPERCONTENTS_SKY) - t->trace->hittexture = &mod_q1bsp_texture_sky; - else if (num & SUPERCONTENTS_LAVA) - t->trace->hittexture = &mod_q1bsp_texture_lava; - else if (num & SUPERCONTENTS_SLIME) - t->trace->hittexture = &mod_q1bsp_texture_slime; - else - t->trace->hittexture = &mod_q1bsp_texture_water; - t->trace->hitq3surfaceflags = t->trace->hittexture->surfaceflags; - t->trace->hitsupercontents = num; - if (num & t->trace->hitsupercontentsmask) + // keep looping until we hit a leaf + while (num >= 0) + { + // find the point distances + node = t->hull->clipnodes + num; + plane = t->hull->planes + node->planenum; + + // axial planes can be calculated more quickly without the DotProduct + if (plane->type < 3) { - // if the first leaf is solid, set startsolid - if (t->trace->allsolid) - t->trace->startsolid = true; -#if COLLISIONPARANOID >= 3 - Con_Print("S"); -#endif - return HULLCHECKSTATE_SOLID; + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; } else { - t->trace->allsolid = false; -#if COLLISIONPARANOID >= 3 - Con_Print("E"); -#endif - return HULLCHECKSTATE_EMPTY; + t1 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (plane->normal, p2) - plane->dist; } - } - - // 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; - } + // negative plane distances indicate children[1] (behind plane) + p1side = t1 < 0; + p2side = t2 < 0; - if (t1 < 0) - { - if (t2 < 0) + // if the line starts and ends on the same side of the plane, recurse + // into that child instantly + if (p1side == p2side) { #if COLLISIONPARANOID >= 3 - Con_Print("<"); + if (p1side) + Con_Print("<"); + else + Con_Print(">"); #endif - num = node->children[1]; - goto loc0; + // loop back and process the start child + num = node->children[p1side]; } - side = 1; - } - else - { - if (t2 >= 0) + else { + // find the midpoint where the line crosses the plane, use the + // original line for best accuracy #if COLLISIONPARANOID >= 3 - Con_Print(">"); + Con_Print("M"); #endif - num = node->children[0]; - goto loc0; - } - side = 0; - } + 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); + + // we now have a mid point, essentially splitting the line into + // the segments in the near child and the far child, we can now + // recurse those in order and get their results + + // recurse both sides, front side first + ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[p1side], p1f, midf, p1, mid); + // if this side is not empty, return what it is (solid or done) + if (ret != HULLCHECKSTATE_EMPTY) + return ret; + + ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[p2side], 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... + + // copy the plane information, flipping it if needed + if (p1side) + { + 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); + } + + // calculate the return fraction which is nudged off the surface a bit + t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist; + t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist; + midf = (t1 - collision_impactnudge.value) / (t1 - t2); + t->trace->fraction = bound(0, midf, 1); - // the line intersects, find intersection point - // LordHavoc: this uses the original trace for maximum accuracy #if COLLISIONPARANOID >= 3 - Con_Print("M"); + Con_Print("D"); #endif - 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; + return HULLCHECKSTATE_DONE; + } } - 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; + // we reached a leaf contents - 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) + // check for empty + num = Mod_Q1BSP_SuperContentsFromNativeContents(NULL, num); + if (!t->trace->startfound) { - t->trace->plane.dist = -plane->dist; - VectorNegate (plane->normal, t->trace->plane.normal); + 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 & SUPERCONTENTS_SOLID) + t->trace->hittexture = &mod_q1bsp_texture_solid; + else if (num & SUPERCONTENTS_SKY) + t->trace->hittexture = &mod_q1bsp_texture_sky; + else if (num & SUPERCONTENTS_LAVA) + t->trace->hittexture = &mod_q1bsp_texture_lava; + else if (num & SUPERCONTENTS_SLIME) + t->trace->hittexture = &mod_q1bsp_texture_slime; else + t->trace->hittexture = &mod_q1bsp_texture_water; + t->trace->hitq3surfaceflags = t->trace->hittexture->surfaceflags; + t->trace->hitsupercontents = num; + if (num & t->trace->hitsupercontentsmask) { - t->trace->plane.dist = plane->dist; - VectorCopy (plane->normal, t->trace->plane.normal); + // if the first leaf is solid, set startsolid + if (t->trace->allsolid) + t->trace->startsolid = true; +#if COLLISIONPARANOID >= 3 + Con_Print("S"); +#endif + return HULLCHECKSTATE_SOLID; } - - // calculate the true fraction - t1 = DotProduct(t->trace->plane.normal, t->start) - t->trace->plane.dist; - t2 = DotProduct(t->trace->plane.normal, t->end) - t->trace->plane.dist; - midf = t1 / (t1 - t2); - t->trace->realfraction = bound(0, midf, 1); - - // calculate the return fraction which is nudged off the surface a bit - midf = (t1 - DIST_EPSILON) / (t1 - t2); - t->trace->fraction = bound(0, midf, 1); - - if (collision_prefernudgedfraction.integer) - t->trace->realfraction = t->trace->fraction; - + else + { + t->trace->allsolid = false; #if COLLISIONPARANOID >= 3 - Con_Print("D"); + Con_Print("E"); #endif - return HULLCHECKSTATE_DONE; + return HULLCHECKSTATE_EMPTY; + } } //#if COLLISIONPARANOID < 2 @@ -895,7 +893,6 @@ static void Mod_Q1BSP_TracePoint(struct model_s *model, const frameblend_t *fram memset(trace, 0, sizeof(trace_t)); rhc.trace = trace; rhc.trace->fraction = 1; - rhc.trace->realfraction = 1; rhc.trace->allsolid = true; rhc.hull = &model->brushq1.hulls[0]; // 0x0x0 VectorCopy(start, rhc.start); @@ -927,7 +924,6 @@ static void Mod_Q1BSP_TraceLine(struct model_s *model, const frameblend_t *frame rhc.trace = trace; rhc.trace->hitsupercontentsmask = hitsupercontentsmask; rhc.trace->fraction = 1; - rhc.trace->realfraction = 1; rhc.trace->allsolid = true; rhc.hull = &model->brushq1.hulls[0]; // 0x0x0 VectorCopy(start, rhc.start); @@ -945,7 +941,6 @@ static void Mod_Q1BSP_TraceLine(struct model_s *model, const frameblend_t *frame rhc.trace = &testtrace; rhc.trace->hitsupercontentsmask = hitsupercontentsmask; rhc.trace->fraction = 1; - rhc.trace->realfraction = 1; rhc.trace->allsolid = true; VectorCopy(test, rhc.start); VectorCopy(test, rhc.end); @@ -984,7 +979,6 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameb rhc.trace = trace; rhc.trace->hitsupercontentsmask = hitsupercontentsmask; rhc.trace->fraction = 1; - rhc.trace->realfraction = 1; rhc.trace->allsolid = true; VectorSubtract(boxmaxs, boxmins, boxsize); if (boxsize[0] < 3) @@ -1027,7 +1021,6 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, const frameblend_t *frameb rhc.trace = &testtrace; rhc.trace->hitsupercontentsmask = hitsupercontentsmask; rhc.trace->fraction = 1; - rhc.trace->realfraction = 1; rhc.trace->allsolid = true; VectorCopy(test, rhc.start); VectorCopy(test, rhc.end); @@ -1096,7 +1089,6 @@ void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cm memset(trace, 0, sizeof(trace_t)); trace->hitsupercontentsmask = hitsupercontentsmask; trace->fraction = 1; - trace->realfraction = 1; Collision_TraceLineBrushFloat(trace, start, end, &cbox, &cbox); #else RecursiveHullCheckTraceInfo_t rhc; @@ -1154,7 +1146,6 @@ void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cm rhc.trace = trace; rhc.trace->hitsupercontentsmask = hitsupercontentsmask; rhc.trace->fraction = 1; - rhc.trace->realfraction = 1; rhc.trace->allsolid = true; VectorCopy(start, rhc.start); VectorCopy(end, rhc.end); @@ -1170,7 +1161,6 @@ void Collision_ClipTrace_Point(trace_t *trace, const vec3_t cmins, const vec3_t { memset(trace, 0, sizeof(trace_t)); trace->fraction = 1; - trace->realfraction = 1; if (BoxesOverlap(start, start, cmins, cmaxs)) { trace->startsupercontents |= boxsupercontents; @@ -1195,48 +1185,45 @@ static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(dp_model_t *model, vec3_t ambie float front, back; float mid, distz = endz - startz; -loc0: - if (!node->plane) - return false; // didn't hit anything - - switch (node->plane->type) + while (node->plane) { - 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; + switch (node->plane->type) + { + case PLANE_X: + node = node->children[x < node->plane->dist]; + continue; // loop back and process the new node + case PLANE_Y: + node = node->children[y < node->plane->dist]; + continue; // loop back and process the new node + case PLANE_Z: + side = startz < node->plane->dist; + if ((endz < node->plane->dist) == side) + { + node = node->children[side]; + continue; // loop back and process the new node + } + // 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]; + continue; // loop back and process the new node + } + // found an intersection + mid = startz + distz * (front - node->plane->dist) / (front - back); + break; } - // found an intersection - mid = startz + distz * (front - node->plane->dist) / (front - back); - break; - } - // go down front side - if (node->children[side]->plane && Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid)) - return true; // hit something - else - { + // go down front side + if (node->children[side]->plane && Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid)) + return true; // hit something + // check for impact on this node if (node->numsurfaces) { @@ -1261,22 +1248,44 @@ loc0: dt = ((x * surface->lightmapinfo->texinfo->vecs[1][0] + y * surface->lightmapinfo->texinfo->vecs[1][1] + mid * surface->lightmapinfo->texinfo->vecs[1][2] + surface->lightmapinfo->texinfo->vecs[1][3]) - surface->lightmapinfo->texturemins[1]) * 0.0625f; // check the bounds - dsi = (int)ds; - dti = (int)dt; + // thanks to jitspoe for pointing out that this int cast was + // rounding toward zero, so we floor it + dsi = (int)floor(ds); + dti = (int)floor(dt); lmwidth = ((surface->lightmapinfo->extents[0]>>4)+1); lmheight = ((surface->lightmapinfo->extents[1]>>4)+1); // is it in bounds? - if (dsi >= 0 && dsi < lmwidth-1 && dti >= 0 && dti < lmheight-1) + // we have to tolerate a position of lmwidth-1 for some rare + // cases - in which case the sampling position still gets + // clamped but the color gets interpolated to that edge. + if (dsi >= 0 && dsi < lmwidth && dti >= 0 && dti < lmheight) { + // in the rare cases where we're sampling slightly off + // the polygon, clamp the sampling position (we can still + // interpolate outside it, where it becomes extrapolation) + if (dsi < 0) + dsi = 0; + if (dti < 0) + dti = 0; + if (dsi > lmwidth-2) + dsi = lmwidth-2; + if (dti > lmheight-2) + dti = lmheight-2; + // calculate bilinear interpolation factors - // and also multiply by fixedpoint conversion factors + // and also multiply by fixedpoint conversion factors to + // compensate for lightmaps being 0-255 (as 0-2), we use + // r_refdef.scene.rtlightstylevalue here which is already + // 0.000-2.148 range + // (if we used r_refdef.scene.lightstylevalue this + // divisor would be 32768 rather than 128) dsfrac = ds - dsi; dtfrac = dt - dti; - w00 = (1 - dsfrac) * (1 - dtfrac) * (1.0f / 32768.0f); - w01 = ( dsfrac) * (1 - dtfrac) * (1.0f / 32768.0f); - w10 = (1 - dsfrac) * ( dtfrac) * (1.0f / 32768.0f); - w11 = ( dsfrac) * ( dtfrac) * (1.0f / 32768.0f); + w00 = (1 - dsfrac) * (1 - dtfrac) * (1.0f / 128.0f); + w01 = ( dsfrac) * (1 - dtfrac) * (1.0f / 128.0f); + w10 = (1 - dsfrac) * ( dtfrac) * (1.0f / 128.0f); + w11 = ( dsfrac) * ( dtfrac) * (1.0f / 128.0f); // values for pointer math line3 = lmwidth * 3; // LordHavoc: *3 for colored lighting @@ -1288,7 +1297,7 @@ loc0: // bilinear filter each lightmap style, and sum them for (maps = 0;maps < MAXLIGHTMAPS && surface->lightmapinfo->styles[maps] != 255;maps++) { - scale = r_refdef.scene.lightstylevalue[surface->lightmapinfo->styles[maps]]; + scale = r_refdef.scene.rtlightstylevalue[surface->lightmapinfo->styles[maps]]; w = w00 * scale;VectorMA(ambientcolor, w, lightmap , ambientcolor); w = w01 * scale;VectorMA(ambientcolor, w, lightmap + 3 , ambientcolor); w = w10 * scale;VectorMA(ambientcolor, w, lightmap + line3 , ambientcolor); @@ -1305,8 +1314,11 @@ loc0: node = node->children[side ^ 1]; startz = mid; distz = endz - startz; - goto loc0; + // loop back and process the new node } + + // did not hit anything + return false; } static void Mod_Q1BSP_LightPoint(dp_model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal) @@ -1373,19 +1385,12 @@ static const texture_t *Mod_Q1BSP_TraceLineAgainstSurfacesFindTextureOnNode(Recu VectorCopy(normal, t->trace->plane.normal); t->trace->plane.dist = DotProduct(normal, p); - // calculate the true fraction + // calculate the return fraction which is nudged off the surface a bit t1 = DotProduct(t->start, t->trace->plane.normal) - t->trace->plane.dist; t2 = DotProduct(t->end, t->trace->plane.normal) - t->trace->plane.dist; - midf = t1 / (t1 - t2); - t->trace->realfraction = midf; - - // calculate the return fraction which is nudged off the surface a bit - midf = (t1 - DIST_EPSILON) / (t1 - t2); + midf = (t1 - collision_impactnudge.value) / (t1 - t2); t->trace->fraction = bound(0, midf, 1); - if (collision_prefernudgedfraction.integer) - t->trace->realfraction = t->trace->fraction; - t->trace->hittexture = surface->texture->currentframe; t->trace->hitq3surfaceflags = t->trace->hittexture->surfaceflags; t->trace->hitsupercontents = t->trace->hittexture->supercontents; @@ -1496,7 +1501,6 @@ static void Mod_Q1BSP_TraceLineAgainstSurfaces(struct model_s *model, const fram rhc.trace = trace; rhc.trace->hitsupercontentsmask = hitsupercontentsmask; rhc.trace->fraction = 1; - rhc.trace->realfraction = 1; rhc.trace->allsolid = true; rhc.hull = &model->brushq1.hulls[0]; // 0x0x0 VectorCopy(start, rhc.start); @@ -1655,6 +1659,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb) strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name)); tx->width = 16; tx->height = 16; + tx->basealpha = 1.0f; if (cls.state != ca_dedicated) { tx->numskinframes = 1; @@ -1727,7 +1732,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb) name[j] = MSG_ReadByte(&miptexsb); name[j] = 0; // pretty up the buffer (replacing any trailing garbage with 0) - for (j = strlen(name);j < 16;j++) + for (j = (int)strlen(name);j < 16;j++) name[j] = 0; if (!name[0]) @@ -1769,6 +1774,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb) strlcpy(tx->name, name, sizeof(tx->name)); tx->width = mtwidth; tx->height = mtheight; + tx->basealpha = 1.0f; if (tx->name[0] == '*') { @@ -1963,7 +1969,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb) for (j = 0;j < max;j++) { tx2 = anims[j]; - tx2->animated = true; + tx2->animated = 1; // q1bsp tx2->anim_total[0] = max; tx2->anim_total[1] = altmax; for (k = 0;k < 10;k++) @@ -1980,7 +1986,7 @@ static void Mod_Q1BSP_LoadTextures(sizebuf_t *sb) for (j = 0;j < altmax;j++) { tx2 = altanims[j]; - tx2->animated = true; + tx2->animated = 1; // q1bsp // the primary/alternate are reversed here tx2->anim_total[0] = altmax; tx2->anim_total[1] = max; @@ -2123,7 +2129,7 @@ static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data) for (i = 0;i < (int)sizeof(value);i++) if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':') break; - if (value[i]) + if (i < (int)sizeof(value) && value[i]) { for (;i < (int)sizeof(value);i++) { @@ -2163,7 +2169,7 @@ static void Mod_Q1BSP_LoadVertexes(sizebuf_t *sb) { mvertex_t *out; int i, count; - size_t structsize = 12; + int structsize = 12; if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name); @@ -2185,7 +2191,7 @@ static void Mod_Q1BSP_LoadSubmodels(sizebuf_t *sb, hullinfo_t *hullinfo) { mmodel_t *out; int i, j, count; - size_t structsize = (48+4*hullinfo->filehulls); + int structsize = (48+4*hullinfo->filehulls); if (sb->cursize % structsize) Host_Error ("Mod_Q1BSP_LoadSubmodels: funny lump size in %s", loadmodel->name); @@ -2220,7 +2226,7 @@ static void Mod_Q1BSP_LoadEdges(sizebuf_t *sb) { medge_t *out; int i, count; - size_t structsize = loadmodel->brush.isbsp2 ? 8 : 4; + int structsize = loadmodel->brush.isbsp2 ? 8 : 4; if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name); @@ -2258,7 +2264,7 @@ static void Mod_Q1BSP_LoadTexinfo(sizebuf_t *sb) { mtexinfo_t *out; int i, j, k, count, miptex; - size_t structsize = 40; + int structsize = 40; if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name); @@ -2275,28 +2281,26 @@ static void Mod_Q1BSP_LoadTexinfo(sizebuf_t *sb) out->vecs[k][j] = MSG_ReadLittleFloat(sb); miptex = MSG_ReadLittleLong(sb); - out->flags = MSG_ReadLittleLong(sb); + out->q1flags = MSG_ReadLittleLong(sb); - out->texture = NULL; - if (loadmodel->data_textures) - { - if ((unsigned int) miptex >= (unsigned int) loadmodel->num_textures) - Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->num_textures); - else - out->texture = loadmodel->data_textures + miptex; - } - if (out->flags & TEX_SPECIAL) + if (out->q1flags & TEX_SPECIAL) { // if texture chosen is NULL or the shader needs a lightmap, // force to notexture water shader - if (out->texture == NULL) - out->texture = loadmodel->data_textures + (loadmodel->num_textures - 1); + out->textureindex = loadmodel->num_textures - 1; } else { // if texture chosen is NULL, force to notexture - if (out->texture == NULL) - out->texture = loadmodel->data_textures + (loadmodel->num_textures - 2); + out->textureindex = loadmodel->num_textures - 2; + } + // see if the specified miptex is valid and try to use it instead + if (loadmodel->data_textures) + { + if ((unsigned int) miptex >= (unsigned int) loadmodel->num_textures) + Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->num_textures); + else + out->textureindex = miptex; } } } @@ -2461,7 +2465,7 @@ static void Mod_Q1BSP_LoadFaces(sizebuf_t *sb) float texmins[2], texmaxs[2], val; rtexture_t *lightmaptexture, *deluxemaptexture; char vabuf[1024]; - size_t structsize = loadmodel->brush.isbsp2 ? 28 : 20; + int structsize = loadmodel->brush.isbsp2 ? 28 : 20; if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name); @@ -2499,6 +2503,7 @@ static void Mod_Q1BSP_LoadFaces(sizebuf_t *sb) for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++) { surface->lightmapinfo = loadmodel->data_surfaces_lightmapinfo + surfacenum; + // the struct on disk is the same in BSP29 (Q1), BSP30 (HL1), and IBSP38 (Q2) planenum = loadmodel->brush.isbsp2 ? MSG_ReadLittleLong(sb) : (unsigned short)MSG_ReadLittleShort(sb); /*side = */loadmodel->brush.isbsp2 ? MSG_ReadLittleLong(sb) : (unsigned short)MSG_ReadLittleShort(sb); firstedge = MSG_ReadLittleLong(sb); @@ -2517,7 +2522,11 @@ static void Mod_Q1BSP_LoadFaces(sizebuf_t *sb) Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)", planenum, loadmodel->brush.num_planes); surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + texinfoindex; - surface->texture = surface->lightmapinfo->texinfo->texture; + surface->texture = loadmodel->data_textures + surface->lightmapinfo->texinfo->textureindex; + + // Q2BSP doesn't use lightmaps on sky or warped surfaces (water), but still has a lightofs of 0 + if (lightmapoffset == 0 && (surface->texture->q2flags & (Q2SURF_SKY | Q2SURF_WARP))) + lightmapoffset = -1; //surface->flags = surface->texture->flags; //if (LittleShort(in->side)) @@ -2593,7 +2602,7 @@ static void Mod_Q1BSP_LoadFaces(sizebuf_t *sb) surface->lightmapinfo->samples = NULL; #if 1 // give non-lightmapped water a 1x white lightmap - if (surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256) + if (!loadmodel->brush.isq2bsp && surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->q1flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256) { surface->lightmapinfo->samples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3); surface->lightmapinfo->styles[0] = 0; @@ -2601,7 +2610,7 @@ static void Mod_Q1BSP_LoadFaces(sizebuf_t *sb) } #endif } - else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30) + else if (loadmodel->brush.ishlbsp || loadmodel->brush.isq2bsp) // LordHavoc: HalfLife map (bsp version 30) surface->lightmapinfo->samples = loadmodel->brushq1.lightdata + lightmapoffset; else // LordHavoc: white lighting (bsp version 29) { @@ -2611,7 +2620,7 @@ static void Mod_Q1BSP_LoadFaces(sizebuf_t *sb) } // check if we should apply a lightmap to this - if (!(surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) || surface->lightmapinfo->samples) + if (!(surface->lightmapinfo->texinfo->q1flags & TEX_SPECIAL) || surface->lightmapinfo->samples) { if (ssize > 256 || tsize > 256) Host_Error("Bad surface extents"); @@ -2769,7 +2778,7 @@ static void Mod_Q1BSP_LoadNodes(sizebuf_t *sb) { int i, j, count, p, child[2]; mnode_t *out; - size_t structsize = loadmodel->brush.isbsp2rmqe ? 32 : (loadmodel->brush.isbsp2 ? 44 : 24); + int structsize = loadmodel->brush.isbsp2rmqe ? 32 : (loadmodel->brush.isbsp2 ? 44 : 24); if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name); @@ -2873,7 +2882,7 @@ static void Mod_Q1BSP_LoadLeafs(sizebuf_t *sb) { mleaf_t *out; int i, j, count, p, firstmarksurface, nummarksurfaces; - size_t structsize = loadmodel->brush.isbsp2rmqe ? 32 : (loadmodel->brush.isbsp2 ? 44 : 28); + int structsize = loadmodel->brush.isbsp2rmqe ? 32 : (loadmodel->brush.isbsp2 ? 44 : 28); if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name); @@ -2989,7 +2998,7 @@ static void Mod_Q1BSP_LoadClipnodes(sizebuf_t *sb, hullinfo_t *hullinfo) mclipnode_t *out; int i, count; hull_t *hull; - size_t structsize = loadmodel->brush.isbsp2 ? 12 : 8; + int structsize = loadmodel->brush.isbsp2 ? 12 : 8; if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name); @@ -3071,7 +3080,7 @@ static void Mod_Q1BSP_MakeHull0(void) static void Mod_Q1BSP_LoadLeaffaces(sizebuf_t *sb) { int i, j; - size_t structsize = loadmodel->brush.isbsp2 ? 4 : 2; + int structsize = loadmodel->brush.isbsp2 ? 4 : 2; if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadLeaffaces: funny lump size in %s",loadmodel->name); @@ -3103,7 +3112,7 @@ static void Mod_Q1BSP_LoadLeaffaces(sizebuf_t *sb) static void Mod_Q1BSP_LoadSurfedges(sizebuf_t *sb) { int i; - size_t structsize = 4; + int structsize = 4; if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name); @@ -3119,7 +3128,7 @@ static void Mod_Q1BSP_LoadPlanes(sizebuf_t *sb) { int i; mplane_t *out; - size_t structsize = 20; + int structsize = 20; if (sb->cursize % structsize) Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name); @@ -3269,7 +3278,7 @@ static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node) static void Mod_Q1BSP_FinalizePortals(void) { - int i, j, numportals, numpoints, portalindex, portalrange = Mem_ExpandableArray_IndexRange(&portalarray); + int i, j, numportals, numpoints, portalindex, portalrange = (int)Mem_ExpandableArray_IndexRange(&portalarray); portal_t *p; mportal_t *portal; mvertex_t *point; @@ -3770,8 +3779,12 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) mod->type = mod_brushq1; - mod->brush.isbsp2 = false; mod->brush.ishlbsp = false; + mod->brush.isbsp2rmqe = false; + mod->brush.isbsp2 = false; + mod->brush.isq2bsp = false; + mod->brush.isq3bsp = false; + mod->brush.skymasking = true; i = MSG_ReadLittleLong(&sb); switch(i) { @@ -3900,6 +3913,7 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) // check if the map supports transparent water rendering loadmodel->brush.supportwateralpha = Mod_Q1BSP_CheckWaterAlphaSupport(); + // we don't need the compressed pvs data anymore if (mod->brushq1.data_compressedpvs) Mem_Free(mod->brushq1.data_compressedpvs); mod->brushq1.data_compressedpvs = NULL; @@ -4114,438 +4128,914 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) Con_DPrintf("Stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals, mesh: %i vertices, %i triangles, %i surfaces\n", loadmodel->name, loadmodel->num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brush.num_pvsclusters, loadmodel->brush.num_portals, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->num_surfaces); } -static void Mod_Q2BSP_LoadEntities(lump_t *l) -{ -} - -static void Mod_Q2BSP_LoadPlanes(lump_t *l) +int Mod_Q2BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativecontents) { -/* - d_t *in; - m_t *out; - int i, count; - - 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)); - - loadmodel-> = out; - loadmodel->num = count; - - for (i = 0;i < count;i++, in++, out++) - { - } -*/ + int supercontents = 0; + if (nativecontents & CONTENTSQ2_SOLID) + supercontents |= SUPERCONTENTS_SOLID; + if (nativecontents & CONTENTSQ2_WATER) + supercontents |= SUPERCONTENTS_WATER; + if (nativecontents & CONTENTSQ2_SLIME) + supercontents |= SUPERCONTENTS_SLIME; + if (nativecontents & CONTENTSQ2_LAVA) + supercontents |= SUPERCONTENTS_LAVA; + if (nativecontents & CONTENTSQ2_MONSTER) + supercontents |= SUPERCONTENTS_BODY; + if (nativecontents & CONTENTSQ2_DEADMONSTER) + supercontents |= SUPERCONTENTS_CORPSE; + if (nativecontents & CONTENTSQ2_PLAYERCLIP) + supercontents |= SUPERCONTENTS_PLAYERCLIP; + if (nativecontents & CONTENTSQ2_MONSTERCLIP) + supercontents |= SUPERCONTENTS_MONSTERCLIP; + if (!(nativecontents & CONTENTSQ2_TRANSLUCENT)) + supercontents |= SUPERCONTENTS_OPAQUE; + return supercontents; } -static void Mod_Q2BSP_LoadVertices(lump_t *l) +int Mod_Q2BSP_NativeContentsFromSuperContents(dp_model_t *model, int supercontents) { -/* - 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)); - - loadmodel-> = out; - loadmodel->num = count; - - for (i = 0;i < count;i++, in++, out++) - { - } -*/ + int nativecontents = 0; + if (supercontents & SUPERCONTENTS_SOLID) + nativecontents |= CONTENTSQ2_SOLID; + if (supercontents & SUPERCONTENTS_WATER) + nativecontents |= CONTENTSQ2_WATER; + if (supercontents & SUPERCONTENTS_SLIME) + nativecontents |= CONTENTSQ2_SLIME; + if (supercontents & SUPERCONTENTS_LAVA) + nativecontents |= CONTENTSQ2_LAVA; + if (supercontents & SUPERCONTENTS_BODY) + nativecontents |= CONTENTSQ2_MONSTER; + if (supercontents & SUPERCONTENTS_CORPSE) + nativecontents |= CONTENTSQ2_DEADMONSTER; + if (supercontents & SUPERCONTENTS_PLAYERCLIP) + nativecontents |= CONTENTSQ2_PLAYERCLIP; + if (supercontents & SUPERCONTENTS_MONSTERCLIP) + nativecontents |= CONTENTSQ2_MONSTERCLIP; + if (!(supercontents & SUPERCONTENTS_OPAQUE)) + nativecontents |= CONTENTSQ2_TRANSLUCENT; + return nativecontents; } -static void Mod_Q2BSP_LoadVisibility(lump_t *l) +static void Mod_Q2BSP_LoadVisibility(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; int i, count; + loadmodel->brushq1.num_compressedpvs = 0; + loadmodel->brushq1.data_compressedpvs = NULL; + loadmodel->brush.num_pvsclusters = 0; + loadmodel->brush.num_pvsclusterbytes = 0; + loadmodel->brush.data_pvsclusters = NULL; - 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)); - - loadmodel-> = out; - loadmodel->num = count; + if (!sb->cursize) + return; - for (i = 0;i < count;i++, in++, out++) + count = MSG_ReadLittleLong(sb); + loadmodel->brush.num_pvsclusters = count; + loadmodel->brush.num_pvsclusterbytes = (count+7)>>3; + loadmodel->brush.data_pvsclusters = (unsigned char *)Mem_Alloc(loadmodel->mempool, count*loadmodel->brush.num_pvsclusterbytes); + for (i = 0;i < count;i++) { + int pvsofs = MSG_ReadLittleLong(sb); + /*int phsofs = */MSG_ReadLittleLong(sb); + // decompress the vis data for this cluster + // (note this accesses the underlying data store of sb, which is kind of evil) + Mod_Q1BSP_DecompressVis(sb->data + pvsofs, sb->data + sb->cursize, loadmodel->brush.data_pvsclusters + i * loadmodel->brush.num_pvsclusterbytes, loadmodel->brush.data_pvsclusters + (i+1) * loadmodel->brush.num_pvsclusterbytes); } -*/ + // hush the loading error check later - we had to do random access on this lump, so we didn't read to the end + sb->readcount = sb->cursize; } -static void Mod_Q2BSP_LoadNodes(lump_t *l) +static void Mod_Q2BSP_LoadNodes(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; - int i, count; + int i, j, count, p, child[2]; + mnode_t *out; + int structsize = 28; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) + if (sb->cursize % structsize) 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)); + count = sb->cursize / structsize; + if (count == 0) + Host_Error("Mod_Q2BSP_LoadNodes: missing BSP tree in %s",loadmodel->name); + out = (mnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); - loadmodel-> = out; - loadmodel->num = count; + loadmodel->brush.data_nodes = out; + loadmodel->brush.num_nodes = count; - for (i = 0;i < count;i++, in++, out++) + for ( i=0 ; iplane = loadmodel->brush.data_planes + p; + child[0] = MSG_ReadLittleLong(sb); + child[1] = MSG_ReadLittleLong(sb); + out->mins[0] = MSG_ReadLittleShort(sb); + out->mins[1] = MSG_ReadLittleShort(sb); + out->mins[2] = MSG_ReadLittleShort(sb); + out->maxs[0] = MSG_ReadLittleShort(sb); + out->maxs[1] = MSG_ReadLittleShort(sb); + out->maxs[2] = MSG_ReadLittleShort(sb); + out->firstsurface = (unsigned short)MSG_ReadLittleShort(sb); + out->numsurfaces = (unsigned short)MSG_ReadLittleShort(sb); + if (out->firstsurface + out->numsurfaces > (unsigned int)loadmodel->num_surfaces) + { + Con_Printf("Mod_Q2BSP_LoadNodes: invalid surface index range %i+%i (file has only %i surfaces)\n", out->firstsurface, out->numsurfaces, loadmodel->num_surfaces); + out->firstsurface = 0; + out->numsurfaces = 0; + } + for (j=0 ; j<2 ; j++) + { + p = child[j]; + if (p >= 0) + { + if (p < loadmodel->brush.num_nodes) + out->children[j] = loadmodel->brush.data_nodes + p; + else + { + Con_Printf("Mod_Q2BSP_LoadNodes: invalid node index %i (file has only %i nodes)\n", p, loadmodel->brush.num_nodes); + // map it to the solid leaf + out->children[j] = (mnode_t *)loadmodel->brush.data_leafs; + } + } + else + { + // get leaf index as a positive value starting at 0 (-1 becomes 0, -2 becomes 1, etc) + p = -(p+1); + if (p < loadmodel->brush.num_leafs) + out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + p); + else + { + Con_Printf("Mod_Q2BSP_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->brush.num_leafs); + // map it to the solid leaf + out->children[j] = (mnode_t *)loadmodel->brush.data_leafs; + } + } + } } -*/ + + Mod_Q1BSP_LoadNodes_RecursiveSetParent(loadmodel->brush.data_nodes, NULL); // sets nodes and leafs } -static void Mod_Q2BSP_LoadTexInfo(lump_t *l) +static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; - int i, count; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - 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)); + mtexinfo_t *out; + int i, j, k, l, count; + int structsize = 76; + int maxtextures = 1024; // hardcoded limit of quake2 engine, so we may as well use it as an upper bound + char filename[MAX_QPATH]; - loadmodel-> = out; - loadmodel->num = count; + if (sb->cursize % structsize) + Host_Error("Mod_Q2BSP_LoadTexinfo: funny lump size in %s",loadmodel->name); + count = sb->cursize / structsize; + out = (mtexinfo_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); + loadmodel->brushq1.texinfo = out; + loadmodel->brushq1.numtexinfo = count; + loadmodel->num_texturesperskin = 0; + loadmodel->data_textures = (texture_t*)Mem_Alloc(loadmodel->mempool, maxtextures * sizeof(texture_t)); - for (i = 0;i < count;i++, in++, out++) + for (i = 0;i < count;i++, out++) { - } -*/ -} - -static void Mod_Q2BSP_LoadFaces(lump_t *l) -{ -/* - d_t *in; - m_t *out; - int i, count; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - 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)); + for (k = 0;k < 2;k++) + for (j = 0;j < 4;j++) + out->vecs[k][j] = MSG_ReadLittleFloat(sb); - loadmodel-> = out; - loadmodel->num = count; + out->q2flags = MSG_ReadLittleLong(sb); + out->q2value = MSG_ReadLittleLong(sb); + MSG_ReadBytes(sb, 32, (unsigned char*)out->q2texture); + out->q2texture[31] = 0; // make absolutely sure it is terminated + out->q2nexttexinfo = MSG_ReadLittleLong(sb); - for (i = 0;i < count;i++, in++, out++) - { - } -*/ -} + // find an existing match for the texture if possible + dpsnprintf(filename, sizeof(filename), "textures/%s.wal", out->q2texture); + for (j = 0;j < loadmodel->num_texturesperskin;j++) + if (!strcmp(filename, loadmodel->data_textures[j].name) && out->q2flags == loadmodel->data_textures[j].q2flags) + break; + // if we don't find the texture, store the new texture + if (j == loadmodel->num_texturesperskin) + { + if (loadmodel->num_texturesperskin < maxtextures) + { + texture_t *tx = loadmodel->data_textures + j; + int q2flags = out->q2flags; + unsigned char *walfile = NULL; + fs_offset_t walfilesize = 0; + Mod_LoadTextureFromQ3Shader(tx, filename, true, true, TEXF_MIPMAP | TEXF_ISWORLD | TEXF_PICMIP | TEXF_COMPRESS); + // now read the .wal file to get metadata (even if a .tga was overriding it, we still need the wal data) + walfile = FS_LoadFile(filename, tempmempool, true, &walfilesize); + if (walfile) + { + int w, h; + char q2animname32c[32]; + LoadWAL_GetMetadata(walfile, (int)walfilesize, &w, &h, &q2flags, &tx->q2value, &tx->q2contents, q2animname32c); + tx->width = w; + tx->height = h; + tx->q2flags = q2flags; + Mem_Free(walfile); + } + // also modify the texture to have the correct contents and such based on flags + // note that we create multiple texture_t structures if q2flags differs + if (q2flags & Q2SURF_LIGHT) + { + // doesn't mean anything to us + } + if (q2flags & Q2SURF_SLICK) + { + // would be nice to support... + } + if (q2flags & Q2SURF_SKY) + { + // sky is a rather specific thing + q2flags &= ~Q2SURF_NODRAW; // quake2 had a slightly different meaning than we have in mind here... + tx->basematerialflags = MATERIALFLAG_SKY | MATERIALFLAG_NOSHADOW; + tx->supercontents = SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP | SUPERCONTENTS_OPAQUE; + tx->surfaceflags = Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT | Q3SURFACEFLAG_NOMARKS | Q3SURFACEFLAG_NODLIGHT | Q3SURFACEFLAG_NOLIGHTMAP; + } + if (q2flags & Q2SURF_WARP) + { + // we use a scroll instead of a warp + tx->basematerialflags |= MATERIALFLAG_WATERSCROLL | MATERIALFLAG_FULLBRIGHT; + // if it's also transparent, we can enable the WATERSHADER + // but we do not set the WATERALPHA flag because we don't + // want to honor r_wateralpha in q2bsp + // (it would go against the artistic intent) + if (q2flags & (Q2SURF_TRANS33 | Q2SURF_TRANS66)) + tx->basematerialflags |= MATERIALFLAG_WATERSHADER; + } + if (q2flags & Q2SURF_TRANS33) + { + tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED; + tx->basealpha = 0.3333f; + tx->supercontents &= ~SUPERCONTENTS_OPAQUE; + if (tx->q2contents & Q2CONTENTS_SOLID) + tx->q2contents = (tx->q2contents & ~Q2CONTENTS_SOLID) | Q2CONTENTS_WINDOW; + } + if (q2flags & Q2SURF_TRANS66) + { + tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED; + tx->basealpha = 0.6667f; + tx->supercontents &= ~SUPERCONTENTS_OPAQUE; + if (tx->q2contents & Q2CONTENTS_SOLID) + tx->q2contents = (tx->q2contents & ~Q2CONTENTS_SOLID) | Q2CONTENTS_WINDOW; + } + if (q2flags & Q2SURF_FLOWING) + { + tx->tcmods[0].tcmod = Q3TCMOD_SCROLL; + tx->tcmods[0].parms[0] = -1.6f; + tx->tcmods[0].parms[1] = 0.0f; + } + if (q2flags & Q2SURF_NODRAW) + { + tx->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW; + } + if (tx->q2contents & (Q2CONTENTS_TRANSLUCENT | Q2CONTENTS_MONSTERCLIP | Q2CONTENTS_PLAYERCLIP)) + tx->q2contents |= Q2CONTENTS_DETAIL; + if (!(tx->q2contents & (Q2CONTENTS_SOLID | Q2CONTENTS_WINDOW | Q2CONTENTS_AUX | Q2CONTENTS_LAVA | Q2CONTENTS_SLIME | Q2CONTENTS_WATER | Q2CONTENTS_MIST | Q2CONTENTS_PLAYERCLIP | Q2CONTENTS_MONSTERCLIP | Q2CONTENTS_MIST))) + tx->q2contents |= Q2CONTENTS_SOLID; + if (tx->q2flags & (Q2SURF_HINT | Q2SURF_SKIP)) + tx->q2contents = 0; + tx->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(loadmodel, tx->q2contents); + // set the current values to the base values + tx->currentframe = tx; + tx->currentskinframe = tx->skinframes[0]; + tx->currentmaterialflags = tx->basematerialflags; + loadmodel->num_texturesperskin++; + loadmodel->num_textures = loadmodel->num_texturesperskin; + } + else + { + Con_Printf("Mod_Q2BSP_LoadTexinfo: max textures reached (%i)\n", maxtextures); + j = 0; // use first texture and give up + } + } + // store the index we found for this texture + out->textureindex = j; + } -static void Mod_Q2BSP_LoadLighting(lump_t *l) -{ -/* - d_t *in; - m_t *out; - int i, count; + // realloc the textures array now that we know how many we actually need + loadmodel->data_textures = (texture_t*)Mem_Realloc(loadmodel->mempool, loadmodel->data_textures, loadmodel->num_texturesperskin * sizeof(texture_t)); - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - 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)); + // now assemble the texture chains + // if we encounter the textures out of order, the later ones won't mark the earlier ones in a sequence, so the earlier + for (i = 0, out = loadmodel->brushq1.texinfo;i < count;i++, out++) + { + int j = i; + texture_t *t = loadmodel->data_textures + out->textureindex; - loadmodel-> = out; - loadmodel->num = count; + // if this is not animated, skip it + // if this is already processed, skip it (part of an existing sequence) + if (out->q2nexttexinfo == 0 || t->animated) + continue; - for (i = 0;i < count;i++, in++, out++) - { + // store the array of frames to use + t->animated = 2; // q2bsp animation + t->anim_total[0] = 0; + t->anim_total[1] = 0; + // gather up to 10 frames (we don't support more) + for (j = i;j >= 0 && t->anim_total[0] < (int)(sizeof(t->anim_frames[0])/sizeof(t->anim_frames[0][0]));j = loadmodel->brushq1.texinfo[j].q2nexttexinfo) + { + // detect looping and stop there + if (t->anim_total[0] && loadmodel->brushq1.texinfo[j].textureindex == out->textureindex) + break; + t->anim_frames[0][t->anim_total[0]++] = &loadmodel->data_textures[loadmodel->brushq1.texinfo[j].textureindex]; + } + // we could look for the +a sequence here if this is the +0 sequence, + // but it seems that quake2 did not implement that (even though the + // files exist in the baseq2 content) + + // write the frame sequence to all the textures involved (just like + // in the q1bsp loader) + // + // note that this can overwrite the rest of the sequence - so if the + // start of a sequence is found later than the other parts of the + // sequence, it will go back and rewrite them correctly. + for (k = 0;k < t->anim_total[0];k++) + { + texture_t *txk = t->anim_frames[0][k]; + txk->animated = t->animated; + txk->anim_total[0] = t->anim_total[0]; + for (l = 0;l < t->anim_total[0];l++) + txk->anim_frames[0][l] = t->anim_frames[0][l]; + } } -*/ } -static void Mod_Q2BSP_LoadLeafs(lump_t *l) +static void Mod_Q2BSP_LoadLighting(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; - int i, count; - - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - 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)); - - loadmodel-> = out; - loadmodel->num = count; - - for (i = 0;i < count;i++, in++, out++) - { - } -*/ + // LordHavoc: this fits exactly the same format that we use in .lit files + loadmodel->brushq1.lightdata = (unsigned char *)Mem_Alloc(loadmodel->mempool, sb->cursize); + MSG_ReadBytes(sb, sb->cursize, loadmodel->brushq1.lightdata); } -static void Mod_Q2BSP_LoadLeafFaces(lump_t *l) +static void Mod_Q2BSP_LoadLeafs(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; - int i, count; + mleaf_t *out; + int i, j, count, firstmarksurface, nummarksurfaces, firstmarkbrush, nummarkbrushes; + int structsize = 28; - 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)); + if (sb->cursize % structsize) + Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name); + count = sb->cursize / structsize; + out = (mleaf_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); - loadmodel-> = out; - loadmodel->num = count; + loadmodel->brush.data_leafs = out; + loadmodel->brush.num_leafs = count; - for (i = 0;i < count;i++, in++, out++) + // FIXME: this function could really benefit from some error checking + for ( i=0 ; icontents = MSG_ReadLittleLong(sb); + out->clusterindex = MSG_ReadLittleShort(sb); + out->areaindex = MSG_ReadLittleShort(sb); + out->mins[0] = MSG_ReadLittleShort(sb); + out->mins[1] = MSG_ReadLittleShort(sb); + out->mins[2] = MSG_ReadLittleShort(sb); + out->maxs[0] = MSG_ReadLittleShort(sb); + out->maxs[1] = MSG_ReadLittleShort(sb); + out->maxs[2] = MSG_ReadLittleShort(sb); + + firstmarksurface = (unsigned short)MSG_ReadLittleShort(sb); + nummarksurfaces = (unsigned short)MSG_ReadLittleShort(sb); + firstmarkbrush = (unsigned short)MSG_ReadLittleShort(sb); + nummarkbrushes = (unsigned short)MSG_ReadLittleShort(sb); -static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l) -{ -/* - d_t *in; - m_t *out; - int i, count; + for (j = 0;j < 4;j++) + out->ambient_sound_level[j] = 0; - 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)); + if (out->clusterindex >= loadmodel->brush.num_pvsclusters) + { + Con_Print("Mod_Q2BSP_LoadLeafs: invalid clusterindex\n"); + out->clusterindex = -1; + } - loadmodel-> = out; - loadmodel->num = count; + if (firstmarksurface >= 0 && firstmarksurface + nummarksurfaces <= loadmodel->brush.num_leafsurfaces) + { + out->firstleafsurface = loadmodel->brush.data_leafsurfaces + firstmarksurface; + out->numleafsurfaces = nummarksurfaces; + } + else + { + Con_Printf("Mod_Q2BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", firstmarksurface, firstmarksurface+nummarksurfaces, 0, loadmodel->brush.num_leafsurfaces); + out->firstleafsurface = NULL; + out->numleafsurfaces = 0; + } - for (i = 0;i < count;i++, in++, out++) - { + if (firstmarkbrush >= 0 && firstmarkbrush + nummarkbrushes <= loadmodel->brush.num_leafbrushes) + { + out->firstleafbrush = loadmodel->brush.data_leafbrushes + firstmarkbrush; + out->numleafbrushes = nummarkbrushes; + } + else + { + Con_Printf("Mod_Q2BSP_LoadLeafs: invalid leafbrush range %i:%i outside range %i:%i\n", firstmarkbrush, firstmarkbrush+nummarkbrushes, 0, loadmodel->brush.num_leafbrushes); + out->firstleafbrush = NULL; + out->numleafbrushes = 0; + } } -*/ } -static void Mod_Q2BSP_LoadEdges(lump_t *l) +static void Mod_Q2BSP_LoadLeafBrushes(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; - int i, count; - - 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)); + int i, j; + int structsize = 2; - loadmodel-> = out; - loadmodel->num = count; + if (sb->cursize % structsize) + Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name); + loadmodel->brush.num_leafbrushes = sb->cursize / structsize; + loadmodel->brush.data_leafbrushes = (int *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_leafbrushes * sizeof(int)); - for (i = 0;i < count;i++, in++, out++) + for (i = 0;i < loadmodel->brush.num_leafbrushes;i++) { + j = (unsigned short) MSG_ReadLittleShort(sb); + if (j >= loadmodel->brush.num_brushes) + Host_Error("Mod_Q1BSP_LoadLeafBrushes: bad brush number"); + loadmodel->brush.data_leafbrushes[i] = j; } -*/ } -static void Mod_Q2BSP_LoadSurfEdges(lump_t *l) +static void Mod_Q2BSP_LoadBrushSides(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; - int i, count; + q3mbrushside_t *out; + int i, n, count; + int structsize = 4; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - 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)); + if (sb->cursize % structsize) + Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name); + count = sb->cursize / structsize; + out = (q3mbrushside_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - loadmodel-> = out; - loadmodel->num = count; + loadmodel->brush.data_brushsides = out; + loadmodel->brush.num_brushsides = count; - for (i = 0;i < count;i++, in++, out++) + for (i = 0;i < count;i++, out++) { + n = (unsigned short)MSG_ReadLittleShort(sb); + if (n < 0 || n >= loadmodel->brush.num_planes) + Host_Error("Mod_Q2BSP_LoadBrushSides: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes); + out->plane = loadmodel->brush.data_planes + n; + n = MSG_ReadLittleShort(sb); + if (n >= 0) + { + if (n >= loadmodel->brushq1.numtexinfo) + Host_Error("Mod_Q2BSP_LoadBrushSides: invalid texinfo index %i (%i texinfos)", n, loadmodel->brushq1.numtexinfo); + out->texture = loadmodel->data_textures + loadmodel->brushq1.texinfo[n].textureindex; + } + else + { + //Con_Printf("Mod_Q2BSP_LoadBrushSides: brushside %i has texinfo index %i < 0, changing to generic texture!\n", i, n); + out->texture = &mod_q1bsp_texture_solid; + } } -*/ } -static void Mod_Q2BSP_LoadBrushes(lump_t *l) +static void Mod_Q2BSP_LoadBrushes(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; - int i, count; + q3mbrush_t *out; + int i, j, firstside, numsides, contents, count, maxplanes, q3surfaceflags, supercontents; + colplanef_t *planes; + int structsize = 12; - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) + if (sb->cursize % structsize) 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)); + count = sb->cursize / structsize; + out = (q3mbrush_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); - loadmodel-> = out; - loadmodel->num = count; + loadmodel->brush.data_brushes = out; + loadmodel->brush.num_brushes = count; - for (i = 0;i < count;i++, in++, out++) - { - } -*/ -} + maxplanes = 0; + planes = NULL; -static void Mod_Q2BSP_LoadBrushSides(lump_t *l) -{ -/* - d_t *in; - m_t *out; - int i, count; + for (i = 0;i < count;i++, out++) + { + firstside = MSG_ReadLittleLong(sb); + numsides = MSG_ReadLittleLong(sb); + contents = MSG_ReadLittleLong(sb); + if (firstside < 0 || firstside + numsides > loadmodel->brush.num_brushsides) + Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)", firstside, firstside + numsides, loadmodel->brush.num_brushsides); - in = (void *)(mod_base + l->fileofs); - if (l->filelen % sizeof(*in)) - 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)); + out->firstbrushside = loadmodel->brush.data_brushsides + firstside; + out->numbrushsides = numsides; + // not really the same... we should store the q2 contents + out->texture = out->firstbrushside->texture; + // convert the contents to our values + supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(loadmodel, contents); - loadmodel-> = out; - loadmodel->num = count; + // make a list of mplane_t structs to construct a colbrush from + if (maxplanes < out->numbrushsides) + { + maxplanes = out->numbrushsides; + if (planes) + Mem_Free(planes); + planes = (colplanef_t *)Mem_Alloc(tempmempool, sizeof(colplanef_t) * maxplanes); + } + q3surfaceflags = 0; + for (j = 0;j < out->numbrushsides;j++) + { + VectorCopy(out->firstbrushside[j].plane->normal, planes[j].normal); + planes[j].dist = out->firstbrushside[j].plane->dist; + planes[j].q3surfaceflags = out->firstbrushside[j].texture->surfaceflags; + planes[j].texture = out->firstbrushside[j].texture; + q3surfaceflags |= planes[j].q3surfaceflags; + // LordHavoc: kind of a mean hack here, but we want the surfaces to have the brush contents + out->firstbrushside[j].texture->supercontents = supercontents; + } + // make the colbrush from the planes + out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, q3surfaceflags, out->texture, true); - for (i = 0;i < count;i++, in++, out++) - { + // this whole loop can take a while (e.g. on redstarrepublic4) + CL_KeepaliveMessage(false); } -*/ + if (planes) + Mem_Free(planes); } -static void Mod_Q2BSP_LoadAreas(lump_t *l) +static void Mod_Q2BSP_LoadPOP(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; - int i, count; - - 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)); + // this is probably a "proof of purchase" lump of some sort, it seems to be 0 size in most bsp files (but not q2dm1.bsp for instance) + sb->readcount = sb->cursize; +} - loadmodel-> = out; - loadmodel->num = count; +static void Mod_Q2BSP_LoadAreas(sizebuf_t *sb) +{ + // we currently don't use areas, they represent closable doors as vis blockers + sb->readcount = sb->cursize; +} - for (i = 0;i < count;i++, in++, out++) - { - } -*/ +static void Mod_Q2BSP_LoadAreaPortals(sizebuf_t *sb) +{ + // we currently don't use areas, they represent closable doors as vis blockers + sb->readcount = sb->cursize; } -static void Mod_Q2BSP_LoadAreaPortals(lump_t *l) +static void Mod_Q2BSP_LoadSubmodels(sizebuf_t *sb) { -/* - d_t *in; - m_t *out; - int i, count; + mmodel_t *out; + int i, count; + int structsize = 48; - 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)); + if (sb->cursize % structsize) + Host_Error ("Mod_Q2BSP_LoadSubmodels: funny lump size in %s", loadmodel->name); - loadmodel-> = out; - loadmodel->num = count; + count = sb->cursize / structsize; + out = (mmodel_t *)Mem_Alloc (loadmodel->mempool, count*sizeof(*out)); - for (i = 0;i < count;i++, in++, out++) + loadmodel->brushq1.submodels = out; + loadmodel->brush.numsubmodels = count; + + // this is identical to the q1 submodel structure except for having 1 hull + for (i = 0; i < count; i++, out++) { + // spread out the mins / maxs by a pixel + out->mins[0] = MSG_ReadLittleFloat(sb) - 1; + out->mins[1] = MSG_ReadLittleFloat(sb) - 1; + out->mins[2] = MSG_ReadLittleFloat(sb) - 1; + out->maxs[0] = MSG_ReadLittleFloat(sb) + 1; + out->maxs[1] = MSG_ReadLittleFloat(sb) + 1; + out->maxs[2] = MSG_ReadLittleFloat(sb) + 1; + out->origin[0] = MSG_ReadLittleFloat(sb); + out->origin[1] = MSG_ReadLittleFloat(sb); + out->origin[2] = MSG_ReadLittleFloat(sb); + out->headnode[0] = MSG_ReadLittleLong(sb); + out->firstface = MSG_ReadLittleLong(sb); + out->numfaces = MSG_ReadLittleLong(sb); } -*/ } -static void Mod_Q2BSP_LoadModels(lump_t *l) +static void Mod_Q2BSP_FindSubmodelBrushRange_r(dp_model_t *mod, mnode_t *node, int *first, int *last) { -/* - 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++) + int i; + mleaf_t *leaf; + while (node->plane) { + Mod_Q2BSP_FindSubmodelBrushRange_r(mod, node->children[0], first, last); + node = node->children[1]; + } + leaf = (mleaf_t*)node; + for (i = 0;i < leaf->numleafbrushes;i++) + { + int brushnum = leaf->firstleafbrush[i]; + if (*first > brushnum) + *first = brushnum; + if (*last < brushnum) + *last = brushnum; } -*/ } static void Mod_Q2BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) { - int i; - q2dheader_t *header; - - Host_Error("Mod_Q2BSP_Load: not yet implemented"); + int i, j, k; + sizebuf_t lumpsb[Q2HEADER_LUMPS]; + mmodel_t *bm; + float dist, modelyawradius, modelradius; + msurface_t *surface; + int totalstylesurfaces, totalstyles, stylecounts[256], remapstyles[256]; + model_brush_lightstyleinfo_t styleinfo[256]; + unsigned char *datapointer; + sizebuf_t sb; - mod->modeldatatypestring = "Q2BSP"; + MSG_InitReadBuffer(&sb, (unsigned char *)buffer, (unsigned char *)bufferend - (unsigned char *)buffer); mod->type = mod_brushq2; - header = (q2dheader_t *)buffer; + mod->brush.ishlbsp = false; + mod->brush.isbsp2rmqe = false; + mod->brush.isbsp2 = false; + mod->brush.isq2bsp = true; // q1bsp loaders mostly work but we need a few tweaks + mod->brush.isq3bsp = false; + mod->brush.skymasking = true; + mod->modeldatatypestring = "Q2BSP"; + + i = MSG_ReadLittleLong(&sb); + if (i != Q2BSPMAGIC) + Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION); - i = LittleLong(header->version); + i = MSG_ReadLittleLong(&sb); if (i != Q2BSPVERSION) Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION); - mod_base = (unsigned char *)header; +// read lumps + for (i = 0; i < Q2HEADER_LUMPS; i++) + { + int offset = MSG_ReadLittleLong(&sb); + int size = MSG_ReadLittleLong(&sb); + if (offset < 0 || offset + size > sb.cursize) + Host_Error("Mod_Q2BSP_Load: %s has invalid lump %i (offset %i, size %i, file size %i)\n", mod->name, i, offset, size, (int)sb.cursize); + MSG_InitReadBuffer(&lumpsb[i], sb.data + offset, size); + } - // swap all the lumps - for (i = 0;i < (int) sizeof(*header) / 4;i++) - ((int *)header)[i] = LittleLong(((int *)header)[i]); + mod->soundfromcenter = true; + mod->TracePoint = Mod_CollisionBIH_TracePoint; + mod->TraceLine = Mod_CollisionBIH_TraceLine; + mod->TraceBox = Mod_CollisionBIH_TraceBox; + mod->TraceBrush = Mod_CollisionBIH_TraceBrush; + mod->PointSuperContents = Mod_CollisionBIH_PointSuperContents; + mod->TraceLineAgainstSurfaces = Mod_CollisionBIH_TraceLine; + mod->brush.TraceLineOfSight = Mod_Q3BSP_TraceLineOfSight; + mod->brush.SuperContentsFromNativeContents = Mod_Q2BSP_SuperContentsFromNativeContents; + mod->brush.NativeContentsFromSuperContents = Mod_Q2BSP_NativeContentsFromSuperContents; + mod->brush.GetPVS = Mod_Q1BSP_GetPVS; + mod->brush.FatPVS = Mod_Q1BSP_FatPVS; + mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS; + mod->brush.BoxTouchingLeafPVS = Mod_Q1BSP_BoxTouchingLeafPVS; + mod->brush.BoxTouchingVisibleLeafs = Mod_Q1BSP_BoxTouchingVisibleLeafs; + mod->brush.FindBoxClusters = Mod_Q1BSP_FindBoxClusters; + mod->brush.LightPoint = Mod_Q1BSP_LightPoint; + mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation; + mod->brush.AmbientSoundLevelsForPoint = NULL; + mod->brush.RoundUpToHullSize = NULL; + mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf; + mod->Draw = R_Q1BSP_Draw; + mod->DrawDepth = R_Q1BSP_DrawDepth; + mod->DrawDebug = R_Q1BSP_DrawDebug; + mod->DrawPrepass = R_Q1BSP_DrawPrepass; + mod->GetLightInfo = R_Q1BSP_GetLightInfo; + mod->CompileShadowMap = R_Q1BSP_CompileShadowMap; + mod->DrawShadowMap = R_Q1BSP_DrawShadowMap; + mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume; + mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume; + mod->DrawLight = R_Q1BSP_DrawLight; + +// load into heap mod->brush.qw_md4sum = 0; mod->brush.qw_md4sum2 = 0; for (i = 0;i < Q2HEADER_LUMPS;i++) { + int temp; if (i == Q2LUMP_ENTITIES) continue; - mod->brush.qw_md4sum ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen); + temp = Com_BlockChecksum(lumpsb[i].data, lumpsb[i].cursize); + mod->brush.qw_md4sum ^= LittleLong(temp); if (i == Q2LUMP_VISIBILITY || i == Q2LUMP_LEAFS || i == Q2LUMP_NODES) continue; - mod->brush.qw_md4sum2 ^= Com_BlockChecksum(mod_base + header->lumps[i].fileofs, header->lumps[i].filelen); + mod->brush.qw_md4sum2 ^= LittleLong(temp); + } + + // many of these functions are identical to Q1 loaders, so we use those where possible + Mod_Q1BSP_LoadEntities(&lumpsb[Q2LUMP_ENTITIES]); + Mod_Q1BSP_LoadVertexes(&lumpsb[Q2LUMP_VERTEXES]); + Mod_Q1BSP_LoadEdges(&lumpsb[Q2LUMP_EDGES]); + Mod_Q1BSP_LoadSurfedges(&lumpsb[Q2LUMP_SURFEDGES]); + Mod_Q2BSP_LoadLighting(&lumpsb[Q2LUMP_LIGHTING]); + Mod_Q1BSP_LoadPlanes(&lumpsb[Q2LUMP_PLANES]); + Mod_Q2BSP_LoadTexinfo(&lumpsb[Q2LUMP_TEXINFO]); + Mod_Q2BSP_LoadBrushSides(&lumpsb[Q2LUMP_BRUSHSIDES]); + Mod_Q2BSP_LoadBrushes(&lumpsb[Q2LUMP_BRUSHES]); + Mod_Q1BSP_LoadFaces(&lumpsb[Q2LUMP_FACES]); + Mod_Q1BSP_LoadLeaffaces(&lumpsb[Q2LUMP_LEAFFACES]); + Mod_Q2BSP_LoadLeafBrushes(&lumpsb[Q2LUMP_LEAFBRUSHES]); + Mod_Q2BSP_LoadVisibility(&lumpsb[Q2LUMP_VISIBILITY]); + Mod_Q2BSP_LoadPOP(&lumpsb[Q2LUMP_POP]); + Mod_Q2BSP_LoadAreas(&lumpsb[Q2LUMP_AREAS]); + Mod_Q2BSP_LoadAreaPortals(&lumpsb[Q2LUMP_AREAPORTALS]); + Mod_Q2BSP_LoadLeafs(&lumpsb[Q2LUMP_LEAFS]); + Mod_Q2BSP_LoadNodes(&lumpsb[Q2LUMP_NODES]); + Mod_Q2BSP_LoadSubmodels(&lumpsb[Q2LUMP_MODELS]); + + for (i = 0; i < Q2HEADER_LUMPS; i++) + if (lumpsb[i].readcount != lumpsb[i].cursize) + Host_Error("Lump %i incorrectly loaded (readcount %i, size %i)\n", i, lumpsb[i].readcount, lumpsb[i].cursize); + + // we don't actually set MATERIALFLAG_WATERALPHA on anything, so this + // doesn't enable the cvar, just indicates that transparent water is OK + loadmodel->brush.supportwateralpha = true; + + // we don't need the compressed pvs data anymore + if (mod->brushq1.data_compressedpvs) + Mem_Free(mod->brushq1.data_compressedpvs); + mod->brushq1.data_compressedpvs = NULL; + mod->brushq1.num_compressedpvs = 0; + + // the MakePortals code works fine on the q2bsp data as well + if (mod_bsp_portalize.integer) + Mod_Q1BSP_MakePortals(); + + mod->numframes = 0; // q2bsp animations are kind of special, frame is unbounded... + mod->numskins = 1; + + // make a single combined shadow mesh to allow optimized shadow volume creation + Mod_Q1BSP_CreateShadowMesh(loadmodel); + + if (loadmodel->brush.numsubmodels) + loadmodel->brush.submodels = (dp_model_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brush.numsubmodels * sizeof(dp_model_t *)); + + totalstylesurfaces = 0; + totalstyles = 0; + for (i = 0;i < mod->brush.numsubmodels;i++) + { + memset(stylecounts, 0, sizeof(stylecounts)); + for (k = 0;k < mod->brushq1.submodels[i].numfaces;k++) + { + surface = mod->data_surfaces + mod->brushq1.submodels[i].firstface + k; + for (j = 0;j < MAXLIGHTMAPS;j++) + stylecounts[surface->lightmapinfo->styles[j]]++; + } + for (k = 0;k < 255;k++) + { + totalstyles++; + if (stylecounts[k]) + totalstylesurfaces += stylecounts[k]; + } + } + datapointer = (unsigned char *)Mem_Alloc(mod->mempool, mod->num_surfaces * sizeof(int) + totalstyles * sizeof(model_brush_lightstyleinfo_t) + totalstylesurfaces * sizeof(int *)); + // set up the world model, then on each submodel copy from the world model + // and set up the submodel with the respective model info. + mod = loadmodel; + for (i = 0;i < loadmodel->brush.numsubmodels;i++) + { + mnode_t *rootnode = NULL; + int firstbrush = loadmodel->brush.num_brushes, lastbrush = 0; + if (i > 0) + { + char name[10]; + // duplicate the basic information + dpsnprintf(name, sizeof(name), "*%i", i); + mod = Mod_FindName(name, loadmodel->name); + // copy the base model to this one + *mod = *loadmodel; + // rename the clone back to its proper name + strlcpy(mod->name, name, sizeof(mod->name)); + mod->brush.parentmodel = loadmodel; + // textures and memory belong to the main model + mod->texturepool = NULL; + mod->mempool = NULL; + mod->brush.GetPVS = NULL; + mod->brush.FatPVS = NULL; + mod->brush.BoxTouchingPVS = NULL; + mod->brush.BoxTouchingLeafPVS = NULL; + mod->brush.BoxTouchingVisibleLeafs = NULL; + mod->brush.FindBoxClusters = NULL; + mod->brush.LightPoint = NULL; + mod->brush.AmbientSoundLevelsForPoint = NULL; + } + mod->brush.submodel = i; + if (loadmodel->brush.submodels) + loadmodel->brush.submodels[i] = mod; + + bm = &mod->brushq1.submodels[i]; + + // we store the headnode (there's only one in Q2BSP) as if it were the first hull + mod->brushq1.hulls[0].firstclipnode = bm->headnode[0]; + + mod->firstmodelsurface = bm->firstface; + mod->nummodelsurfaces = bm->numfaces; + + // set node/leaf parents for this submodel + // note: if the root of this submodel is a leaf (headnode[0] < 0) then there is nothing to do... + // (this happens in base3.bsp) + if (bm->headnode[0] >= 0) + rootnode = mod->brush.data_nodes + bm->headnode[0]; + else + rootnode = (mnode_t*)(mod->brush.data_leafs + -1 - bm->headnode[0]); + Mod_Q1BSP_LoadNodes_RecursiveSetParent(rootnode, NULL); + + // make the model surface list (used by shadowing/lighting) + mod->sortedmodelsurfaces = (int *)datapointer;datapointer += mod->nummodelsurfaces * sizeof(int); + Mod_Q2BSP_FindSubmodelBrushRange_r(mod, rootnode, &firstbrush, &lastbrush); + if (firstbrush <= lastbrush) + { + mod->firstmodelbrush = firstbrush; + mod->nummodelbrushes = lastbrush + 1 - firstbrush; + } + else + { + mod->firstmodelbrush = 0; + mod->nummodelbrushes = 0; + } + Mod_MakeSortedSurfaces(mod); + + VectorCopy(bm->mins, mod->normalmins); + VectorCopy(bm->maxs, mod->normalmaxs); + dist = max(fabs(mod->normalmins[0]), fabs(mod->normalmaxs[0])); + modelyawradius = max(fabs(mod->normalmins[1]), fabs(mod->normalmaxs[1])); + modelyawradius = dist*dist+modelyawradius*modelyawradius; + modelradius = max(fabs(mod->normalmins[2]), fabs(mod->normalmaxs[2])); + modelradius = modelyawradius + modelradius * modelradius; + modelyawradius = sqrt(modelyawradius); + modelradius = sqrt(modelradius); + mod->yawmins[0] = mod->yawmins[1] = -modelyawradius; + mod->yawmins[2] = mod->normalmins[2]; + mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius; + 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; + + // this gets altered below if sky or water is used + mod->DrawSky = NULL; + mod->DrawAddWaterPlanes = NULL; + + // scan surfaces for sky and water and flag the submodel as possessing these features or not + // build lightstyle lists for quick marking of dirty lightmaps when lightstyles flicker + if (mod->nummodelsurfaces) + { + for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++) + if (surface->texture->basematerialflags & MATERIALFLAG_SKY) + break; + if (j < mod->nummodelsurfaces) + mod->DrawSky = R_Q1BSP_DrawSky; + + for (j = 0, surface = &mod->data_surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surface++) + if (surface->texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION | MATERIALFLAG_CAMERA)) + break; + if (j < mod->nummodelsurfaces) + mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes; + + // build lightstyle update chains + // (used to rapidly mark lightmapupdateflags on many surfaces + // when d_lightstylevalue changes) + memset(stylecounts, 0, sizeof(stylecounts)); + for (k = 0;k < mod->nummodelsurfaces;k++) + { + surface = mod->data_surfaces + mod->firstmodelsurface + k; + for (j = 0;j < MAXLIGHTMAPS;j++) + stylecounts[surface->lightmapinfo->styles[j]]++; + } + mod->brushq1.num_lightstyles = 0; + for (k = 0;k < 255;k++) + { + if (stylecounts[k]) + { + styleinfo[mod->brushq1.num_lightstyles].style = k; + styleinfo[mod->brushq1.num_lightstyles].value = 0; + styleinfo[mod->brushq1.num_lightstyles].numsurfaces = 0; + styleinfo[mod->brushq1.num_lightstyles].surfacelist = (int *)datapointer;datapointer += stylecounts[k] * sizeof(int); + remapstyles[k] = mod->brushq1.num_lightstyles; + mod->brushq1.num_lightstyles++; + } + } + for (k = 0;k < mod->nummodelsurfaces;k++) + { + surface = mod->data_surfaces + mod->firstmodelsurface + k; + for (j = 0;j < MAXLIGHTMAPS;j++) + { + if (surface->lightmapinfo->styles[j] != 255) + { + int r = remapstyles[surface->lightmapinfo->styles[j]]; + styleinfo[r].surfacelist[styleinfo[r].numsurfaces++] = mod->firstmodelsurface + k; + } + } + } + mod->brushq1.data_lightstyleinfo = (model_brush_lightstyleinfo_t *)datapointer;datapointer += mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t); + memcpy(mod->brushq1.data_lightstyleinfo, styleinfo, mod->brushq1.num_lightstyles * sizeof(model_brush_lightstyleinfo_t)); + } + else + { + Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadmodel->name); + } + //mod->brushq1.num_visleafs = bm->visleafs; + + // build a Bounding Interval Hierarchy for culling triangles in light rendering + Mod_MakeCollisionBIH(mod, false, &mod->collision_bih); + + // build a Bounding Interval Hierarchy for culling brushes in collision detection + Mod_MakeCollisionBIH(mod, true, &mod->render_bih); + + // generate VBOs and other shared data before cloning submodels + if (i == 0) + Mod_BuildVBOs(); } + mod = loadmodel; - 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]); + Con_DPrintf("Stats for q2bsp model \"%s\": %i faces, %i nodes, %i leafs, %i clusters, %i clusterportals, mesh: %i vertices, %i triangles, %i surfaces\n", loadmodel->name, loadmodel->num_surfaces, loadmodel->brush.num_nodes, loadmodel->brush.num_leafs, mod->brush.num_pvsclusters, loadmodel->brush.num_portals, loadmodel->surfmesh.num_vertices, loadmodel->surfmesh.num_triangles, loadmodel->num_surfaces); } static int Mod_Q3BSP_SuperContentsFromNativeContents(dp_model_t *model, int nativecontents); @@ -4636,10 +5126,9 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) for (i = 0;i < count;i++) { - strlcpy (out[i].name, in[i].name, sizeof (out[i].name)); out[i].surfaceflags = LittleLong(in[i].surfaceflags); out[i].supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, LittleLong(in[i].contents)); - Mod_LoadTextureFromQ3Shader(out + i, out[i].name, true, true, TEXF_MIPMAP | TEXF_ISWORLD | TEXF_PICMIP | TEXF_COMPRESS); + Mod_LoadTextureFromQ3Shader(out + i, in[i].name, true, true, TEXF_MIPMAP | TEXF_ISWORLD | TEXF_PICMIP | TEXF_COMPRESS); // restore the surfaceflags and supercontents out[i].surfaceflags = LittleLong(in[i].surfaceflags); out[i].supercontents = Mod_Q3BSP_SuperContentsFromNativeContents(loadmodel, LittleLong(in[i].contents)); @@ -6218,7 +6707,6 @@ void Mod_CollisionBIH_TracePoint(dp_model_t *model, const frameblend_t *frameble memset(trace, 0, sizeof(*trace)); trace->fraction = 1; - trace->realfraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; bih = &model->collision_bih; @@ -6294,7 +6782,6 @@ static void Mod_CollisionBIH_TraceLineShared(dp_model_t *model, const frameblend memset(trace, 0, sizeof(*trace)); trace->fraction = 1; - trace->realfraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; // push first node @@ -6311,7 +6798,12 @@ static void Mod_CollisionBIH_TraceLineShared(dp_model_t *model, const frameblend node = bih->nodes + nodenum; VectorCopy(nodestackline[nodestackpos], nodestart); VectorCopy(nodestackline[nodestackpos] + 3, nodeend); - sweepnodemins[0] = min(nodestart[0], nodeend[0]); sweepnodemins[1] = min(nodestart[1], nodeend[1]); sweepnodemins[2] = min(nodestart[2], nodeend[2]); sweepnodemaxs[0] = max(nodestart[0], nodeend[0]); sweepnodemaxs[1] = max(nodestart[1], nodeend[1]); sweepnodemaxs[2] = max(nodestart[2], nodeend[2]); + sweepnodemins[0] = min(nodestart[0], nodeend[0]) - 1; + sweepnodemins[1] = min(nodestart[1], nodeend[1]) - 1; + sweepnodemins[2] = min(nodestart[2], nodeend[2]) - 1; + sweepnodemaxs[0] = max(nodestart[0], nodeend[0]) + 1; + sweepnodemaxs[1] = max(nodestart[1], nodeend[1]) + 1; + sweepnodemaxs[2] = max(nodestart[2], nodeend[2]) + 1; if (!BoxesOverlap(sweepnodemins, sweepnodemaxs, node->mins, node->maxs)) continue; if (node->type <= BIH_SPLITZ && nodestackpos+2 <= 1024) @@ -6354,7 +6846,12 @@ static void Mod_CollisionBIH_TraceLineShared(dp_model_t *model, const frameblend axis = 2; d1 = nodestart[axis] - nodebigmins[axis]; d2 = nodeend[axis] - nodebigmins[axis]; if (d1 < 0) { if (d2 < 0) continue; f = d1 / (d1 - d2); VectorLerp(nodestart, f, nodeend, nodestart); } else if (d2 < 0) { f = d1 / (d1 - d2); VectorLerp(nodestart, f, nodeend, nodeend); } d1 = nodebigmaxs[axis] - nodestart[axis]; d2 = nodebigmaxs[axis] - nodeend[axis]; if (d1 < 0) { if (d2 < 0) continue; f = d1 / (d1 - d2); VectorLerp(nodestart, f, nodeend, nodestart); } else if (d2 < 0) { f = d1 / (d1 - d2); VectorLerp(nodestart, f, nodeend, nodeend); } // some of the line intersected the enlarged node box // calculate sweep bounds for this node - sweepnodemins[0] = min(nodestart[0], nodeend[0]); sweepnodemins[1] = min(nodestart[1], nodeend[1]); sweepnodemins[2] = min(nodestart[2], nodeend[2]); sweepnodemaxs[0] = max(nodestart[0], nodeend[0]); sweepnodemaxs[1] = max(nodestart[1], nodeend[1]); sweepnodemaxs[2] = max(nodestart[2], nodeend[2]); + sweepnodemins[0] = min(nodestart[0], nodeend[0]) - 1; + sweepnodemins[1] = min(nodestart[1], nodeend[1]) - 1; + sweepnodemins[2] = min(nodestart[2], nodeend[2]) - 1; + sweepnodemaxs[0] = max(nodestart[0], nodeend[0]) + 1; + sweepnodemaxs[1] = max(nodestart[1], nodeend[1]) + 1; + sweepnodemaxs[2] = max(nodestart[2], nodeend[2]) + 1; for (axis = 0;axis < BIH_MAXUNORDEREDCHILDREN && node->children[axis] >= 0;axis++) { leaf = bih->leafs + node->children[axis]; @@ -6424,7 +6921,6 @@ void Mod_CollisionBIH_TraceBrush(dp_model_t *model, const frameblend_t *frameble // box trace, performed as brush trace memset(trace, 0, sizeof(*trace)); trace->fraction = 1; - trace->realfraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; // calculate tracebox-like parameters for efficient culling @@ -6455,7 +6951,12 @@ void Mod_CollisionBIH_TraceBrush(dp_model_t *model, const frameblend_t *frameble node = bih->nodes + nodenum; VectorCopy(nodestackline[nodestackpos], nodestart); VectorCopy(nodestackline[nodestackpos] + 3, nodeend); - sweepnodemins[0] = min(nodestart[0], nodeend[0]) + mins[0]; sweepnodemins[1] = min(nodestart[1], nodeend[1]) + mins[1]; sweepnodemins[2] = min(nodestart[2], nodeend[2]) + mins[2]; sweepnodemaxs[0] = max(nodestart[0], nodeend[0]) + maxs[0]; sweepnodemaxs[1] = max(nodestart[1], nodeend[1]) + maxs[1]; sweepnodemaxs[2] = max(nodestart[2], nodeend[2]) + maxs[2]; + sweepnodemins[0] = min(nodestart[0], nodeend[0]) + mins[0] - 1; + sweepnodemins[1] = min(nodestart[1], nodeend[1]) + mins[1] - 1; + sweepnodemins[2] = min(nodestart[2], nodeend[2]) + mins[2] - 1; + sweepnodemaxs[0] = max(nodestart[0], nodeend[0]) + maxs[0] + 1; + sweepnodemaxs[1] = max(nodestart[1], nodeend[1]) + maxs[1] + 1; + sweepnodemaxs[2] = max(nodestart[2], nodeend[2]) + maxs[2] + 1; if (!BoxesOverlap(sweepnodemins, sweepnodemaxs, node->mins, node->maxs)) continue; if (node->type <= BIH_SPLITZ && nodestackpos+2 <= 1024) @@ -6498,7 +6999,12 @@ void Mod_CollisionBIH_TraceBrush(dp_model_t *model, const frameblend_t *frameble axis = 2; d1 = nodestart[axis] - nodebigmins[axis]; d2 = nodeend[axis] - nodebigmins[axis]; if (d1 < 0) { if (d2 < 0) continue; f = d1 / (d1 - d2); VectorLerp(nodestart, f, nodeend, nodestart); } else if (d2 < 0) { f = d1 / (d1 - d2); VectorLerp(nodestart, f, nodeend, nodeend); } d1 = nodebigmaxs[axis] - nodestart[axis]; d2 = nodebigmaxs[axis] - nodeend[axis]; if (d1 < 0) { if (d2 < 0) continue; f = d1 / (d1 - d2); VectorLerp(nodestart, f, nodeend, nodestart); } else if (d2 < 0) { f = d1 / (d1 - d2); VectorLerp(nodestart, f, nodeend, nodeend); } // some of the line intersected the enlarged node box // calculate sweep bounds for this node - sweepnodemins[0] = min(nodestart[0], nodeend[0]) + mins[0]; sweepnodemins[1] = min(nodestart[1], nodeend[1]) + mins[1]; sweepnodemins[2] = min(nodestart[2], nodeend[2]) + mins[2]; sweepnodemaxs[0] = max(nodestart[0], nodeend[0]) + maxs[0]; sweepnodemaxs[1] = max(nodestart[1], nodeend[1]) + maxs[1]; sweepnodemaxs[2] = max(nodestart[2], nodeend[2]) + maxs[2]; + sweepnodemins[0] = min(nodestart[0], nodeend[0]) + mins[0] - 1; + sweepnodemins[1] = min(nodestart[1], nodeend[1]) + mins[1] - 1; + sweepnodemins[2] = min(nodestart[2], nodeend[2]) + mins[2] - 1; + sweepnodemaxs[0] = max(nodestart[0], nodeend[0]) + maxs[0] + 1; + sweepnodemaxs[1] = max(nodestart[1], nodeend[1]) + maxs[1] + 1; + sweepnodemaxs[2] = max(nodestart[2], nodeend[2]) + maxs[2] + 1; for (axis = 0;axis < BIH_MAXUNORDEREDCHILDREN && node->children[axis] >= 0;axis++) { leaf = bih->leafs + node->children[axis]; @@ -6544,13 +7050,20 @@ void Mod_CollisionBIH_TraceBox(dp_model_t *model, const frameblend_t *frameblend } -static int Mod_CollisionBIH_PointSuperContents(struct model_s *model, int frame, const vec3_t point) +int Mod_CollisionBIH_PointSuperContents(struct model_s *model, int frame, const vec3_t point) { trace_t trace; Mod_CollisionBIH_TracePoint(model, NULL, NULL, &trace, point, 0); return trace.startsupercontents; } +qboolean Mod_CollisionBIH_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end) +{ + trace_t trace; + Mod_CollisionBIH_TraceLine(model, NULL, NULL, &trace, start, end, SUPERCONTENTS_VISBLOCKERMASK); + return trace.fraction == 1; +} + void Mod_CollisionBIH_TracePoint_Mesh(dp_model_t *model, const frameblend_t *frameblend, const skeleton_t *skeleton, trace_t *trace, const vec3_t start, int hitsupercontentsmask) { #if 0 @@ -6561,14 +7074,12 @@ void Mod_CollisionBIH_TracePoint_Mesh(dp_model_t *model, const frameblend_t *fra #endif memset(trace, 0, sizeof(*trace)); trace->fraction = 1; - trace->realfraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; #if 0 Mod_CollisionBIH_TraceLine(model, frameblend, skeleton, trace, start, end, hitsupercontentsmask); hitsupercontents = trace->hitsupercontents; memset(trace, 0, sizeof(*trace)); trace->fraction = 1; - trace->realfraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; trace->startsupercontents = hitsupercontents; #endif @@ -6583,7 +7094,6 @@ int Mod_CollisionBIH_PointSuperContents_Mesh(struct model_s *model, int frame, c VectorSet(end, start[0], start[1], model->normalmins[2]); memset(&trace, 0, sizeof(trace)); trace.fraction = 1; - trace.realfraction = 1; trace.hitsupercontentsmask = 0; Mod_CollisionBIH_TraceLine(model, frameblend, skeleton, trace, start, end, hitsupercontentsmask); return trace.hitsupercontents; @@ -6665,7 +7175,7 @@ static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, dp_model_t *mod Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[startside], start, mid, startfrac, midfrac, linestart, lineend, markframe, segmentmins, segmentmaxs); // if we found an impact on the front side, don't waste time // exploring the far side - if (midfrac <= trace->realfraction) + if (midfrac <= trace->fraction) Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace, model, node->children[endside], mid, end, midfrac, endfrac, linestart, lineend, markframe, segmentmins, segmentmaxs); return; } @@ -6813,7 +7323,6 @@ static void Mod_Q3BSP_TracePoint(dp_model_t *model, const frameblend_t *frameble q3mbrush_t *brush; memset(trace, 0, sizeof(*trace)); trace->fraction = 1; - trace->realfraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; if (mod_collision_bih.integer) Mod_CollisionBIH_TracePoint(model, frameblend, skeleton, trace, start, hitsupercontentsmask); @@ -6842,7 +7351,6 @@ static void Mod_Q3BSP_TraceLine(dp_model_t *model, const frameblend_t *frameblen memset(trace, 0, sizeof(*trace)); trace->fraction = 1; - trace->realfraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; segmentmins[0] = min(start[0], end[0]) - 1; segmentmins[1] = min(start[1], end[1]) - 1; @@ -6885,14 +7393,13 @@ static void Mod_Q3BSP_TraceBrush(dp_model_t *model, const frameblend_t *frameble // box trace, performed as brush trace memset(trace, 0, sizeof(*trace)); trace->fraction = 1; - trace->realfraction = 1; trace->hitsupercontentsmask = hitsupercontentsmask; - segmentmins[0] = min(start->mins[0], end->mins[0]); - segmentmins[1] = min(start->mins[1], end->mins[1]); - segmentmins[2] = min(start->mins[2], end->mins[2]); - segmentmaxs[0] = max(start->maxs[0], end->maxs[0]); - segmentmaxs[1] = max(start->maxs[1], end->maxs[1]); - segmentmaxs[2] = max(start->maxs[2], end->maxs[2]); + segmentmins[0] = min(start->mins[0], end->mins[0]) - 1; + segmentmins[1] = min(start->mins[1], end->mins[1]) - 1; + segmentmins[2] = min(start->mins[2], end->mins[2]) - 1; + segmentmaxs[0] = max(start->maxs[0], end->maxs[0]) + 1; + segmentmaxs[1] = max(start->maxs[1], end->maxs[1]) + 1; + segmentmaxs[2] = max(start->maxs[2], end->maxs[2]) + 1; if (mod_collision_bih.integer) Mod_CollisionBIH_TraceBrush(model, frameblend, skeleton, trace, start, end, hitsupercontentsmask); else if (model->brush.submodel) @@ -7183,6 +7690,11 @@ static void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) mod->modeldatatypestring = "Q3BSP"; mod->type = mod_brushq3; + mod->brush.ishlbsp = false; + mod->brush.isbsp2rmqe = false; + mod->brush.isbsp2 = false; + mod->brush.isq2bsp = false; + mod->brush.isq3bsp = true; mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1 mod->numskins = 1;