cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1", "enables collisions with curves (SLOW)"};
cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1", "whether to use optimized traceline code for line traces (as opposed to tracebox code)"};
cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0", "selects different tracebrush bsp recursion algorithms (for debugging purposes only)"};
+cvar_t mod_q3bsp_lightmapmergepower = {CVAR_SAVE, "mod_q3bsp_lightmapmergepower", "4", "merges the quake3 128x128 lightmap textures into larger lightmap group textures to speed up rendering, 1 = 256x256, 2 = 512x512, 3 = 1024x1024, 4 = 2048x2048, 5 = 4096x4096, ..."};
static texture_t mod_q1bsp_texture_solid;
static texture_t mod_q1bsp_texture_sky;
Cvar_RegisterVariable(&mod_q3bsp_curves_collisions);
Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
+ Cvar_RegisterVariable(&mod_q3bsp_lightmapmergepower);
memset(&mod_q1bsp_texture_solid, 0, sizeof(mod_q1bsp_texture_solid));
strlcpy(mod_q1bsp_texture_solid.name, "solid" , sizeof(mod_q1bsp_texture_solid.name));
#endif
}
+static int Mod_Q1BSP_TraceLineOfSight_RecursiveNodeCheck(mnode_t *node, double p1[3], double p2[3])
+{
+ double t1, t2;
+ double midf, mid[3];
+ int ret, side;
+
+ // check for empty
+ while (node->plane)
+ {
+ // find the point distances
+ mplane_t *plane = node->plane;
+ 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;
+ }
+
+ if (t1 < 0)
+ {
+ if (t2 < 0)
+ {
+ node = node->children[1];
+ continue;
+ }
+ side = 1;
+ }
+ else
+ {
+ if (t2 >= 0)
+ {
+ node = node->children[0];
+ continue;
+ }
+ side = 0;
+ }
+
+ midf = t1 / (t1 - t2);
+ VectorLerp(p1, midf, p2, mid);
+
+ // recurse both sides, front side first
+ // return 2 if empty is followed by solid (hit something)
+ // do not return 2 if both are solid or both empty,
+ // or if start is solid and end is empty
+ // as these degenerate cases usually indicate the eye is in solid and
+ // should see the target point anyway
+ ret = Mod_Q1BSP_TraceLineOfSight_RecursiveNodeCheck(node->children[side ], p1, mid);
+ if (ret != 0)
+ return ret;
+ ret = Mod_Q1BSP_TraceLineOfSight_RecursiveNodeCheck(node->children[side ^ 1], mid, p2);
+ if (ret != 1)
+ return ret;
+ return 2;
+ }
+ return ((mleaf_t *)node)->clusterindex < 0;
+}
+
+static qboolean Mod_Q1BSP_TraceLineOfSight(struct model_s *model, const vec3_t start, const vec3_t end)
+{
+ // this function currently only supports same size start and end
+ double tracestart[3], traceend[3];
+ VectorCopy(start, tracestart);
+ VectorCopy(end, traceend);
+ return Mod_Q1BSP_TraceLineOfSight_RecursiveNodeCheck(model->brush.data_nodes, tracestart, traceend) != 2;
+}
+
static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(model_t *model, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
{
int side;
void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
{
- Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2] + 0.125, p[2] - 65536);
// pretend lighting is coming down from above (due to lack of a lightgrid to know primary lighting direction)
VectorSet(diffusenormal, 0, 0, 1);
+
+ if (!model->brushq1.lightdata)
+ {
+ VectorSet(ambientcolor, 1, 1, 1);
+ VectorSet(diffusecolor, 0, 0, 0);
+ return;
+ }
+
+ Mod_Q1BSP_LightPoint_RecursiveBSPNode(model, ambientcolor, diffusecolor, diffusenormal, model->brush.data_nodes + model->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2] + 0.125, p[2] - 65536);
}
static void Mod_Q1BSP_DecompressVis(const unsigned char *in, const unsigned char *inend, unsigned char *out, unsigned char *outend)
tx->skinframerate = 1;
tx->currentskinframe = tx->skinframes;
tx->skinframes[0].base = r_texture_notexture;
+ tx->backgroundcurrentskinframe = tx->backgroundskinframes;
tx->basematerialflags = 0;
if (i == loadmodel->num_textures - 1)
{
node->parent = parent;
if (node->plane)
{
+ // this is a node, recurse to children
Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
Mod_Q1BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
+ // combine supercontents of children
+ node->combinedsupercontents = node->children[0]->combinedsupercontents | node->children[1]->combinedsupercontents;
+ }
+ else
+ {
+ int j;
+ mleaf_t *leaf = (mleaf_t *)node;
+ // if this is a leaf, calculate supercontents mask from all collidable
+ // primitives in the leaf (brushes and collision surfaces)
+ // also flag if the leaf contains any collision surfaces
+ leaf->combinedsupercontents = 0;
+ // combine the supercontents values of all brushes in this leaf
+ for (j = 0;j < leaf->numleafbrushes;j++)
+ leaf->combinedsupercontents |= loadmodel->brush.data_brushes[leaf->firstleafbrush[j]].texture->supercontents;
+ // check if this leaf contains any collision surfaces (q3 patches)
+ for (j = 0;j < leaf->numleafsurfaces;j++)
+ {
+ msurface_t *surface = loadmodel->data_surfaces + leaf->firstleafsurface[j];
+ if (surface->num_collisiontriangles)
+ {
+ leaf->containscollisionsurfaces = true;
+ leaf->combinedsupercontents |= surface->texture->supercontents;
+ }
+ }
}
}
{
pvs = loadmodel->brush.data_pvsclusters + leaf->clusterindex * loadmodel->brush.num_pvsclusterbytes;
for (j = 0;j < loadmodel->brush.num_leafs;j++)
- if (leaf->contents == CONTENTS_EMPTY && CHECKPVSBIT(pvs, loadmodel->brush.data_leafs[j].clusterindex))
+ if (CHECKPVSBIT(pvs, loadmodel->brush.data_leafs[j].clusterindex) && loadmodel->brush.data_leafs[j].contents == CONTENTS_EMPTY)
return true;
}
}
mod->soundfromcenter = true;
mod->TraceBox = Mod_Q1BSP_TraceBox;
+ mod->brush.TraceLineOfSight = Mod_Q1BSP_TraceLineOfSight;
mod->brush.SuperContentsFromNativeContents = Mod_Q1BSP_SuperContentsFromNativeContents;
mod->brush.NativeContentsFromSuperContents = Mod_Q1BSP_NativeContentsFromSuperContents;
mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
// check if the map supports transparent water rendering
loadmodel->brush.supportwateralpha = Mod_Q1BSP_CheckWaterAlphaSupport();
- if (!mod->brushq1.lightdata)
- mod->brush.LightPoint = NULL;
-
if (mod->brushq1.data_compressedpvs)
Mem_Free(mod->brushq1.data_compressedpvs);
mod->brushq1.data_compressedpvs = NULL;
mod->DrawLight = R_Q1BSP_DrawLight;
if (i != 0)
{
+ mod->brush.TraceLineOfSight = NULL;
mod->brush.GetPVS = NULL;
mod->brush.FatPVS = NULL;
mod->brush.BoxTouchingPVS = NULL;
{
int i;
layer->numframes = min(numparameters - 2, TEXTURE_MAXFRAMES);
- layer->framerate = atoi(parameter[1]);
+ layer->framerate = atof(parameter[1]);
for (i = 0;i < layer->numframes;i++)
strlcpy(layer->texturename[i], parameter[i + 2], sizeof(layer->texturename));
}
shader->surfaceparms |= Q3SURFACEPARM_WATER;
else if (!strcasecmp(parameter[1], "pointlight"))
shader->surfaceparms |= Q3SURFACEPARM_POINTLIGHT;
+ else if (!strcasecmp(parameter[1], "antiportal"))
+ shader->surfaceparms |= Q3SURFACEPARM_ANTIPORTAL;
else
- Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
+ Con_DPrintf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[fileindex], parameter[1]);
}
else if (!strcasecmp(parameter[0], "sky") && numparameters >= 2)
{
// identify if this is a blended terrain shader or similar
if (shader->numlayers)
{
+ shader->backgroundlayer = NULL;
shader->primarylayer = shader->layers + 0;
- if ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA) || shader->layers[1].alphatest)
+ if ((shader->layers[0].blendfunc[0] == GL_ONE && shader->layers[0].blendfunc[1] == GL_ZERO && !shader->layers[0].alphatest)
+ && ((shader->layers[1].blendfunc[0] == GL_SRC_ALPHA && shader->layers[1].blendfunc[1] == GL_ONE_MINUS_SRC_ALPHA && !shader->layers[0].alphatest)
+ || (shader->layers[1].blendfunc[0] == GL_ONE && shader->layers[1].blendfunc[1] == GL_ZERO && shader->layers[1].alphatest)))
{
// terrain blending or other effects
shader->backgroundlayer = shader->layers + 0;
}
// now see if the lightmap came first, and if so choose the second texture instead
if (!strcasecmp(shader->primarylayer->texturename[0], "$lightmap"))
+ {
+ shader->backgroundlayer = NULL;
shader->primarylayer = shader->layers + 1;
+ }
}
}
Mem_Free(f);
out->basematerialflags |= MATERIALFLAG_WALL;
if (shader->layers[0].alphatest)
out->basematerialflags |= MATERIALFLAG_ALPHATEST | MATERIALFLAG_TRANSPARENT | MATERIALFLAG_NOSHADOW;
- if (shader->textureflags & (Q3TEXTUREFLAG_TWOSIDED | Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
+ if (shader->textureflags & Q3TEXTUREFLAG_TWOSIDED)
+ out->basematerialflags |= MATERIALFLAG_NOSHADOW | MATERIALFLAG_NOCULLFACE;
+ if (shader->textureflags & (Q3TEXTUREFLAG_AUTOSPRITE | Q3TEXTUREFLAG_AUTOSPRITE2))
out->basematerialflags |= MATERIALFLAG_NOSHADOW;
out->customblendfunc[0] = GL_ONE;
out->customblendfunc[1] = GL_ZERO;
if (!Mod_LoadSkinFrame(&out->skinframes[j], shader->primarylayer->texturename[j], ((shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (shader->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP) | (shader->primarylayer->clampmap ? TEXF_CLAMP : 0), false, true))
Con_Printf("%s: could not load texture \"%s\" (frame %i) for shader \"%s\"\n", loadmodel->name, shader->primarylayer->texturename[j], j, out->name);
}
+ if (shader->backgroundlayer && cls.state != ca_dedicated)
+ {
+ int j;
+ out->backgroundnumskinframes = shader->backgroundlayer->numframes;
+ out->backgroundskinframerate = shader->backgroundlayer->framerate;
+ for (j = 0;j < shader->backgroundlayer->numframes;j++)
+ if (!Mod_LoadSkinFrame(&out->backgroundskinframes[j], shader->backgroundlayer->texturename[j], ((shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (shader->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP) | (shader->backgroundlayer->clampmap ? TEXF_CLAMP : 0), false, true))
+ Con_Printf("%s: could not load texture \"%s\" (frame %i) for shader \"%s\"\n", loadmodel->name, shader->backgroundlayer->texturename[j], j, out->name);
+ }
}
+ else if (!strcmp(out->name, "noshader"))
+ out->surfaceparms = 0;
else
{
c++;
// init the animation variables
out->currentframe = out;
out->currentskinframe = &out->skinframes[0];
+ out->backgroundcurrentskinframe = &out->backgroundskinframes[0];
}
if (c)
Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
}
}
-static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
+static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
{
q3dlightmap_t *in;
- rtexture_t **out;
- int i, count;
+ int i, j, count, power, power2, mask, endlightmap;
unsigned char *c;
if (!l->filelen)
return;
+ if (cls.state == ca_dedicated)
+ return;
in = (q3dlightmap_t *)(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 = (rtexture_t **)Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
-
- loadmodel->brushq3.data_lightmaps = out;
- loadmodel->brushq3.num_lightmaps = count;
-
- // deluxemapped q3bsp files have an even number of lightmaps, and surfaces
- // always index even numbered ones (0, 2, 4, ...), the odd numbered
- // lightmaps are the deluxemaps (light direction textures), so if we
- // encounter any odd numbered lightmaps it is not a deluxemapped bsp, it
- // is also not a deluxemapped bsp if it has an odd number of lightmaps or
- // less than 2
- loadmodel->brushq3.deluxemapping = true;
+ loadmodel->brushq3.num_originallightmaps = count;
+
+ // now check the surfaces to see if any of them index an odd numbered
+ // lightmap, if so this is not a deluxemapped bsp file
+ //
+ // also check what lightmaps are actually used, because q3map2 sometimes
+ // (always?) makes an unused one at the end, which
+ // q3map2 sometimes (or always?) makes a second blank lightmap for no
+ // reason when only one lightmap is used, which can throw off the
+ // deluxemapping detection method, so check 2-lightmap bsp's specifically
+ // to see if the second lightmap is blank, if so it is not deluxemapped.
+ loadmodel->brushq3.deluxemapping = !(count & 1);
loadmodel->brushq3.deluxemapping_modelspace = true;
- if (count < 2 || (count & 1))
+ endlightmap = 0;
+ if (loadmodel->brushq3.deluxemapping)
+ {
+ int facecount = faceslump->filelen / sizeof(q3dface_t);
+ q3dface_t *faces = (q3dface_t *)(mod_base + faceslump->fileofs);
+ for (i = 0;i < facecount;i++)
+ {
+ j = LittleLong(faces[i].lightmapindex);
+ if (j >= 0)
+ {
+ endlightmap = max(endlightmap, j + 1);
+ if ((j & 1) || j + 1 >= count)
+ {
+ loadmodel->brushq3.deluxemapping = false;
+ break;
+ }
+ }
+ }
+ }
+ if (endlightmap < 2)
loadmodel->brushq3.deluxemapping = false;
// q3map2 sometimes (or always?) makes a second blank lightmap for no
// reason when only one lightmap is used, which can throw off the
// deluxemapping detection method, so check 2-lightmap bsp's specifically
// to see if the second lightmap is blank, if so it is not deluxemapped.
- if (count == 2)
+ if (endlightmap == 1 && count == 2)
{
- c = in[count - 1].rgb;
+ c = in[1].rgb;
for (i = 0;i < 128*128*3;i++)
if (c[i])
break;
}
}
- // further deluxemapping detection is done in Mod_Q3BSP_LoadFaces
+ Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not ");
+
+ // figure out what the most reasonable merge power is within limits
+ loadmodel->brushq3.num_lightmapmergepower = 0;
+ for (power = 1;power <= mod_q3bsp_lightmapmergepower.integer && (1 << power) <= gl_max_texture_size && (1 << (power * 2)) < 4 * (count >> loadmodel->brushq3.deluxemapping);power++)
+ loadmodel->brushq3.num_lightmapmergepower = power;
+ loadmodel->brushq3.num_lightmapmerge = 1 << loadmodel->brushq3.num_lightmapmergepower;
- 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);
+ loadmodel->brushq3.num_mergedlightmaps = ((count >> loadmodel->brushq3.deluxemapping) + (1 << (loadmodel->brushq3.num_lightmapmergepower * 2)) - 1) >> (loadmodel->brushq3.num_lightmapmergepower * 2);
+ loadmodel->brushq3.data_lightmaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
+ if (loadmodel->brushq3.deluxemapping)
+ loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
+
+ j = 128 << loadmodel->brushq3.num_lightmapmergepower;
+ if (loadmodel->brushq3.data_lightmaps)
+ for (i = 0;i < loadmodel->brushq3.num_mergedlightmaps;i++)
+ loadmodel->brushq3.data_lightmaps[i] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), j, j, NULL, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+
+ if (loadmodel->brushq3.data_deluxemaps)
+ for (i = 0;i < loadmodel->brushq3.num_mergedlightmaps;i++)
+ loadmodel->brushq3.data_deluxemaps[i] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", i), j, j, NULL, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+
+ power = loadmodel->brushq3.num_lightmapmergepower;
+ power2 = power * 2;
+ mask = (1 << power) - 1;
+ for (i = 0;i < count;i++)
+ {
+ j = i >> loadmodel->brushq3.deluxemapping;
+ if (loadmodel->brushq3.deluxemapping && (i & 1))
+ R_UpdateTexture(loadmodel->brushq3.data_deluxemaps[j >> power2], in[i].rgb, (j & mask) * 128, ((j >> power) & mask) * 128, 128, 128);
+ else
+ R_UpdateTexture(loadmodel->brushq3.data_lightmaps [j >> power2], in[i].rgb, (j & mask) * 128, ((j >> power) & mask) * 128, 128, 128);
+ }
}
static void Mod_Q3BSP_LoadFaces(lump_t *l)
q3dface_t *in, *oldin;
msurface_t *out, *oldout;
int i, oldi, j, n, count, invalidelements, patchsize[2], finalwidth, finalheight, xtess, ytess, finalvertices, finaltriangles, firstvertex, firstelement, type, oldnumtriangles, oldnumtriangles2, meshvertices, meshtriangles, numvertices, numtriangles;
+ float lightmaptcbase[2], lightmaptcscale;
//int *originalelement3i;
//int *originalneighbor3i;
float *originalvertex3f;
loadmodel->data_surfaces = out;
loadmodel->num_surfaces = count;
- // now that we have surfaces to look at, see if any of them index an odd numbered lightmap, if so this is not a deluxemapped bsp file
- if (loadmodel->brushq3.deluxemapping)
- {
- for (i = 0;i < count;i++)
- {
- n = LittleLong(in[i].lightmapindex);
- if (n >= 0 && ((n & 1) || n + 1 >= loadmodel->brushq3.num_lightmaps))
- {
- loadmodel->brushq3.deluxemapping = false;
- break;
- }
- }
- }
- Con_DPrintf("%s is %sdeluxemapped\n", loadmodel->name, loadmodel->brushq3.deluxemapping ? "" : "not ");
-
i = 0;
oldi = i;
oldin = in;
out->effect = NULL;
else
out->effect = loadmodel->brushq3.data_effects + n;
- n = LittleLong(in->lightmapindex);
- if (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;
- }
- else if (n < 0)
- n = -1;
- if (n == -1)
+
+ if (cls.state != ca_dedicated)
{
out->lightmaptexture = NULL;
out->deluxemaptexture = r_texture_blanknormalmap;
- }
- else
- {
- out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
- if (loadmodel->brushq3.deluxemapping)
- out->deluxemaptexture = loadmodel->brushq3.data_lightmaps[n+1];
+ n = LittleLong(in->lightmapindex);
+ if (n < 0)
+ n = -1;
+ else if (n >= loadmodel->brushq3.num_originallightmaps)
+ {
+ Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_originallightmaps);
+ n = -1;
+ }
else
- out->deluxemaptexture = r_texture_blanknormalmap;
+ {
+ out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n >> (loadmodel->brushq3.num_lightmapmergepower * 2 + loadmodel->brushq3.deluxemapping)];
+ if (loadmodel->brushq3.deluxemapping)
+ out->deluxemaptexture = loadmodel->brushq3.data_deluxemaps[n >> (loadmodel->brushq3.num_lightmapmergepower * 2 + loadmodel->brushq3.deluxemapping)];
+ }
}
firstvertex = LittleLong(in->firstvertex);
{
case Q3FACETYPE_POLYGON:
case Q3FACETYPE_MESH:
- // no processing necessary
+ // no processing necessary, except for lightmap merging
for (j = 0;j < out->num_vertices;j++)
{
(loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex)[j * 3 + 0] = loadmodel->brushq3.data_vertex3f[(firstvertex + j) * 3 + 0];
VectorClear(out->maxs);
if (out->num_vertices)
{
+ int lightmapindex = LittleLong(in->lightmapindex);
+ if (lightmapindex >= 0 && cls.state != ca_dedicated)
+ {
+ lightmapindex >>= loadmodel->brushq3.deluxemapping;
+ lightmaptcscale = 1.0f / loadmodel->brushq3.num_lightmapmerge;
+ lightmaptcbase[0] = ((lightmapindex ) & (loadmodel->brushq3.num_lightmapmerge - 1)) * lightmaptcscale;
+ lightmaptcbase[1] = ((lightmapindex >> loadmodel->brushq3.num_lightmapmergepower) & (loadmodel->brushq3.num_lightmapmerge - 1)) * lightmaptcscale;
+ // modify the lightmap texcoords to match this region of the merged lightmap
+ for (j = 0, v = loadmodel->surfmesh.data_texcoordlightmap2f + 2 * out->num_firstvertex;j < out->num_vertices;j++, v += 2)
+ {
+ v[0] = v[0] * lightmaptcscale + lightmaptcbase[0];
+ v[1] = v[1] * lightmaptcscale + lightmaptcbase[1];
+ }
+ }
VectorCopy((loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), out->mins);
VectorCopy((loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex), out->maxs);
for (j = 1, v = (loadmodel->surfmesh.data_vertex3f + 3 * out->num_firstvertex) + 3;j < out->num_vertices;j++, v += 3)
// walk the tree until we hit a leaf, recursing for any split cases
while (node->plane)
{
+ // abort if this part of the bsp tree can not be hit by this trace
+// if (!(node->combinedsupercontents & trace->hitsupercontentsmask))
+// return;
plane = node->plane;
// axial planes are much more common than non-axial, so an optimized
// axial case pays off here
return;
}
}
+ // abort if this part of the bsp tree can not be hit by this trace
+// if (!(node->combinedsupercontents & trace->hitsupercontentsmask))
+// return;
// hit a leaf
nodesegmentmins[0] = min(start[0], end[0]) - 1;
nodesegmentmins[1] = min(start[1], end[1]) - 1;
}
}
// can't do point traces on curves (they have no thickness)
- if (mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
+ if (leaf->containscollisionsurfaces && mod_q3bsp_curves_collisions.integer && !VectorCompare(start, end))
{
// line trace the curves
for (i = 0;i < leaf->numleafsurfaces;i++)
// walk the tree until we hit a leaf, recursing for any split cases
while (node->plane)
{
+ // abort if this part of the bsp tree can not be hit by this trace
+// if (!(node->combinedsupercontents & trace->hitsupercontentsmask))
+// return;
plane = node->plane;
// axial planes are much more common than non-axial, so an optimized
// axial case pays off here
// take whichever side the segment box is on
node = node->children[sides - 1];
}
+ // abort if this part of the bsp tree can not be hit by this trace
+// if (!(node->combinedsupercontents & trace->hitsupercontentsmask))
+// return;
nodesegmentmins[0] = max(segmentmins[0], node->mins[0] - 1);
nodesegmentmins[1] = max(segmentmins[1], node->mins[1] - 1);
nodesegmentmins[2] = max(segmentmins[2], node->mins[2] - 1);
Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, brush, brush);
}
}
- if (mod_q3bsp_curves_collisions.integer)
+ if (leaf->containscollisionsurfaces && mod_q3bsp_curves_collisions.integer)
{
for (i = 0;i < leaf->numleafsurfaces;i++)
{
mod->soundfromcenter = true;
mod->TraceBox = Mod_Q3BSP_TraceBox;
+ mod->brush.TraceLineOfSight = Mod_Q1BSP_TraceLineOfSight;
mod->brush.SuperContentsFromNativeContents = Mod_Q3BSP_SuperContentsFromNativeContents;
mod->brush.NativeContentsFromSuperContents = Mod_Q3BSP_NativeContentsFromSuperContents;
mod->brush.GetPVS = Mod_Q1BSP_GetPVS;
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_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS], &header->lumps[Q3LUMP_FACES]);
Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
// textures and memory belong to the main model
mod->texturepool = NULL;
mod->mempool = NULL;
+ mod->brush.TraceLineOfSight = NULL;
mod->brush.GetPVS = NULL;
mod->brush.FatPVS = NULL;
mod->brush.BoxTouchingPVS = NULL;
break;
if (j < mod->nummodelsurfaces)
mod->DrawSky = R_Q1BSP_DrawSky;
+ else
+ mod->DrawSky = NULL;
}
}
Host_Error("Mod_MAP_Load: not yet implemented");
}
+qboolean Mod_CanSeeBox_Trace(int numsamples, float t, model_t *model, vec3_t eye, vec3_t minsX, vec3_t maxsX)
+{
+ // we already have done PVS culling at this point...
+ // so we don't need to do it again.
+
+ int i;
+ vec3_t testorigin, mins, maxs;
+
+ testorigin[0] = (minsX[0] + maxsX[0]) * 0.5;
+ testorigin[1] = (minsX[1] + maxsX[1]) * 0.5;
+ testorigin[2] = (minsX[2] + maxsX[2]) * 0.5;
+
+ if(model->brush.TraceLineOfSight(model, eye, testorigin))
+ return 1;
+
+ // expand the box a little
+ mins[0] = (t+1) * minsX[0] - t * maxsX[0];
+ maxs[0] = (t+1) * maxsX[0] - t * minsX[0];
+ mins[1] = (t+1) * minsX[1] - t * maxsX[1];
+ maxs[1] = (t+1) * maxsX[1] - t * minsX[1];
+ mins[2] = (t+1) * minsX[2] - t * maxsX[2];
+ maxs[2] = (t+1) * maxsX[2] - t * minsX[2];
+
+ for(i = 0; i != numsamples; ++i)
+ {
+ testorigin[0] = lhrandom(mins[0], maxs[0]);
+ testorigin[1] = lhrandom(mins[1], maxs[1]);
+ testorigin[2] = lhrandom(mins[2], maxs[2]);
+
+ if(model->brush.TraceLineOfSight(model, eye, testorigin))
+ return 1;
+ }
+
+ return 0;
+}
+