X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=blobdiff_plain;f=model_brush.c;h=1f0349875080a4507d49eb54db0df65459ef3b13;hp=4db80c951ddfbf77924cbc8e234d92bc10797fbe;hb=43c93c6edf2dc67b6315ebddc7a71c7f8c0597d8;hpb=f98ed219932ae83f6f8352af46db08db7602274a diff --git a/model_brush.c b/model_brush.c index 4db80c95..1f034987 100644 --- a/model_brush.c +++ b/model_brush.c @@ -93,45 +93,51 @@ static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, memset(out, 0, outsize); } - -static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) +static int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) { - int leafnum; -loc0: - if (node->contents < 0) - { - // leaf - if (node->contents == CONTENTS_SOLID) - return false; - leafnum = (mleaf_t *)node - model->brushq1.leafs - 1; - return pvs[leafnum >> 3] & (1 << (leafnum & 7)); - } - - // node - recurse down the BSP tree - switch (BoxOnPlaneSide(mins, maxs, node->plane)) + int clusterindex, side, nodestackindex = 0; + mnode_t *node, *nodestack[1024]; + node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode; + for (;;) { - case 1: // front - node = node->children[0]; - goto loc0; - case 2: // back - node = node->children[1]; - goto loc0; - default: // crossing - if (node->children[0]->contents != CONTENTS_SOLID) - if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs)) + if (node->plane) + { + // node - recurse down the BSP tree + side = BoxOnPlaneSide(mins, maxs, node->plane) - 1; + if (side < 2) + { + // box is on one side of plane, take that path + node = node->children[side]; + } + else + { + // box crosses plane, take one path and remember the other + nodestack[nodestackindex++] = node->children[0]; + node = node->children[1]; + } + } + else + { + // leaf - check cluster bit + clusterindex = ((mleaf_t *)node)->clusterindex; + if (CHECKPVSBIT(pvs, clusterindex)) + { + // it is visible, return immediately with the news return true; - node = node->children[1]; - goto loc0; + } + else + { + // nothing to see here, try another path we didn't take earlier + if (nodestackindex == 0) + break; + node = nodestack[--nodestackindex]; + } + } } - // never reached + // it is not visible return false; } -int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) -{ - return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs); -} - /* static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p) { @@ -1957,7 +1963,7 @@ static void Mod_Q1BSP_LoadNodes(lump_t *l) if (p >= 0) out->children[j] = loadmodel->brushq1.nodes + p; else - out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p)); + out->children[j] = (mnode_t *)(loadmodel->brushq1.data_leafs + (-1 - p)); } } @@ -1968,8 +1974,7 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l) { dleaf_t *in; mleaf_t *out; - int i, j, count, p, pvschainbytes; - qbyte *pvs; + int i, j, count, p; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) @@ -1977,11 +1982,13 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l) count = l->filelen / sizeof(*in); out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out)); - loadmodel->brushq1.leafs = out; - loadmodel->brushq1.numleafs = count; + loadmodel->brushq1.data_leafs = out; + loadmodel->brushq1.num_leafs = count; // get visleafs from the submodel data - pvschainbytes = (loadmodel->brushq1.submodels[0].visleafs+7)>>3; - loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes); + loadmodel->brushq1.num_pvsclusters = loadmodel->brushq1.submodels[0].visleafs; + loadmodel->brushq1.num_pvsclusterbytes = (loadmodel->brushq1.num_pvsclusters+7)>>3; + loadmodel->brushq1.data_pvsclusters = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.num_pvsclusters * loadmodel->brushq1.num_pvsclusterbytes); + memset(loadmodel->brushq1.data_pvsclusters, 0xFF, loadmodel->brushq1.num_pvsclusters * loadmodel->brushq1.num_pvsclusterbytes); for ( i=0 ; inummarksurfaces = 0; } - out->pvsdata = pvs; - memset(out->pvsdata, 0xFF, pvschainbytes); - pvs += pvschainbytes; + out->clusterindex = i - 1; + if (out->clusterindex >= loadmodel->brushq1.num_pvsclusters) + out->clusterindex = -1; p = LittleLong(in->visofs); - if (p >= 0 && i > 0) // ignore visofs errors on leaf 0 (solid) + // ignore visofs errors on leaf 0 (solid) + if (p >= 0 && out->clusterindex >= 0) { if (p >= loadmodel->brushq1.num_compressedpvs) Con_Printf("Mod_Q1BSP_LoadLeafs: invalid visofs\n"); else - Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes); + Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, loadmodel->brushq1.data_pvsclusters + out->clusterindex * loadmodel->brushq1.num_pvsclusterbytes, loadmodel->brushq1.data_pvsclusters + (out->clusterindex + 1) * loadmodel->brushq1.num_pvsclusterbytes); } for (j = 0;j < 4;j++) @@ -2263,8 +2271,8 @@ static void Mod_Q1BSP_FinalizePortals(void) winding_t *w; // recalculate bounding boxes for all leafs(because qbsp is very sloppy) - leaf = loadmodel->brushq1.leafs; - endleaf = leaf + loadmodel->brushq1.numleafs; + leaf = loadmodel->brushq1.data_leafs; + endleaf = leaf + loadmodel->brushq1.num_leafs; for (;leaf < endleaf;leaf++) { VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000); @@ -2317,8 +2325,8 @@ static void Mod_Q1BSP_FinalizePortals(void) loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t)); loadmodel->brushq1.numportalpoints = numpoints; // clear all leaf portal chains - for (i = 0;i < loadmodel->brushq1.numleafs;i++) - loadmodel->brushq1.leafs[i].portals = NULL; + for (i = 0;i < loadmodel->brushq1.num_leafs;i++) + loadmodel->brushq1.data_leafs[i].portals = NULL; // process all portals in the global portal chain, while freeing them portal = loadmodel->brushq1.portals; point = loadmodel->brushq1.portalpoints; @@ -2723,12 +2731,9 @@ static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model) static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node) { - int i; - float d; - - while (node->contents >= 0) + while (node->plane) { - d = PlaneDiff(org, node->plane); + float d = PlaneDiff(org, node->plane); if (d > radius) node = node->children[0]; else if (d < -radius) @@ -2740,18 +2745,21 @@ static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, node = node->children[1]; } } - // FIXME: code! - // if this is a leaf, accumulate the pvs bits - if (/*node->contents != CONTENTS_SOLID && */((mleaf_t *)node)->pvsdata) + // if this leaf is in a cluster, accumulate the pvs bits + if (((mleaf_t *)node)->clusterindex >= 0) + { + int i; + qbyte *pvs = model->brushq1.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brushq1.num_pvsclusterbytes; for (i = 0;i < pvsbytes;i++) - pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i]; + pvsbuffer[i] |= pvs[i]; + } } //Calculates a PVS that is the inclusive or of all leafs within radius pixels //of the given point. static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength) { - int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3; + int bytes = ((model->brushq1.num_leafs - 1) + 7) >> 3; bytes = min(bytes, pvsbufferlength); if (r_novis.integer) { @@ -2764,17 +2772,18 @@ static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyt } //Returns PVS data for a given point -//(note: always returns valid data, never NULL) +//(note: can return NULL) static qbyte *Mod_Q1BSP_GetPVS(model_t *model, const vec3_t p) { mnode_t *node; Mod_CheckLoaded(model); - // LordHavoc: modified to start at first clip node, - // in other words: first node of the (sub)model - node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode; - while (node->contents == 0) + node = model->brushq1.nodes; + while (node->plane) node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist]; - return ((mleaf_t *)node)->pvsdata; + if (((mleaf_t *)node)->clusterindex >= 0) + return model->brushq1.data_pvsclusters + ((mleaf_t *)node)->clusterindex * model->brushq1.num_pvsclusterbytes; + else + return NULL; } static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs) @@ -2813,7 +2822,7 @@ static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, co extern void R_Model_Brush_DrawSky(entity_render_t *ent); extern void R_Model_Brush_Draw(entity_render_t *ent); extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius); -extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz); +extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap); void Mod_Q1BSP_Load(model_t *mod, void *buffer) { int i, j, k; @@ -2921,6 +2930,14 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) mod->Draw = R_Model_Brush_Draw; mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume; mod->DrawLight = R_Model_Brush_DrawLight; + if (i != 0) + { + mod->brush.GetPVS = NULL; + mod->brush.FatPVS = NULL; + mod->brush.BoxTouchingPVS = NULL; + mod->brush.LightPoint = NULL; + mod->brush.AmbientSoundLevelsForPoint = NULL; + } mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **)); mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *)); mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int)); @@ -2975,7 +2992,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) } Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool); - mod->brushq1.visleafs = bm->visleafs; + mod->brushq1.num_visleafs = bm->visleafs; // LordHavoc: only register submodels if it is the world // (prevents bsp models from replacing world submodels) @@ -2998,7 +3015,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer) //Mod_Q1BSP_ProcessLightList(); if (developer.integer) - Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.visleafs, loadmodel->brushq1.numportals); + Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.num_leafs, loadmodel->brushq1.num_visleafs, loadmodel->brushq1.numportals); } static void Mod_Q2BSP_LoadEntities(lump_t *l) @@ -3518,79 +3535,79 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) { snprintf(shadername, sizeof(shadername), "%s", com_token); flags = 0; - if (COM_ParseToken(&text, false) && !strcmp(com_token, "{")) + if (COM_ParseToken(&text, false) && !strcasecmp(com_token, "{")) { while (COM_ParseToken(&text, false)) { - if (!strcmp(com_token, "}")) + if (!strcasecmp(com_token, "}")) break; - else if (!strcmp(com_token, "{")) + else if (!strcasecmp(com_token, "{")) { while (COM_ParseToken(&text, false)) { - if (!strcmp(com_token, "}")) + if (!strcasecmp(com_token, "}")) break; } } - else if (!strcmp(com_token, "surfaceparm")) + else if (!strcasecmp(com_token, "surfaceparm")) { - if (COM_ParseToken(&text, true) && strcmp(com_token, "\n")) + if (COM_ParseToken(&text, true) && strcasecmp(com_token, "\n")) { - if (!strcmp(com_token, "alphashadow")) + if (!strcasecmp(com_token, "alphashadow")) flags |= Q3SURFACEPARM_ALPHASHADOW; - else if (!strcmp(com_token, "areaportal")) + else if (!strcasecmp(com_token, "areaportal")) flags |= Q3SURFACEPARM_AREAPORTAL; - else if (!strcmp(com_token, "clusterportal")) + else if (!strcasecmp(com_token, "clusterportal")) flags |= Q3SURFACEPARM_CLUSTERPORTAL; - else if (!strcmp(com_token, "detail")) + else if (!strcasecmp(com_token, "detail")) flags |= Q3SURFACEPARM_DETAIL; - else if (!strcmp(com_token, "donotenter")) + else if (!strcasecmp(com_token, "donotenter")) flags |= Q3SURFACEPARM_DONOTENTER; - else if (!strcmp(com_token, "fog")) + else if (!strcasecmp(com_token, "fog")) flags |= Q3SURFACEPARM_FOG; - else if (!strcmp(com_token, "lava")) + else if (!strcasecmp(com_token, "lava")) flags |= Q3SURFACEPARM_LAVA; - else if (!strcmp(com_token, "lightfilter")) + else if (!strcasecmp(com_token, "lightfilter")) flags |= Q3SURFACEPARM_LIGHTFILTER; - else if (!strcmp(com_token, "metalsteps")) + else if (!strcasecmp(com_token, "metalsteps")) flags |= Q3SURFACEPARM_METALSTEPS; - else if (!strcmp(com_token, "nodamage")) + else if (!strcasecmp(com_token, "nodamage")) flags |= Q3SURFACEPARM_NODAMAGE; - else if (!strcmp(com_token, "nodlight")) + else if (!strcasecmp(com_token, "nodlight")) flags |= Q3SURFACEPARM_NODLIGHT; - else if (!strcmp(com_token, "nodraw")) + else if (!strcasecmp(com_token, "nodraw")) flags |= Q3SURFACEPARM_NODRAW; - else if (!strcmp(com_token, "nodrop")) + else if (!strcasecmp(com_token, "nodrop")) flags |= Q3SURFACEPARM_NODROP; - else if (!strcmp(com_token, "noimpact")) + else if (!strcasecmp(com_token, "noimpact")) flags |= Q3SURFACEPARM_NOIMPACT; - else if (!strcmp(com_token, "nolightmap")) + else if (!strcasecmp(com_token, "nolightmap")) flags |= Q3SURFACEPARM_NOLIGHTMAP; - else if (!strcmp(com_token, "nomarks")) + else if (!strcasecmp(com_token, "nomarks")) flags |= Q3SURFACEPARM_NOMARKS; - else if (!strcmp(com_token, "nomipmaps")) + else if (!strcasecmp(com_token, "nomipmaps")) flags |= Q3SURFACEPARM_NOMIPMAPS; - else if (!strcmp(com_token, "nonsolid")) + else if (!strcasecmp(com_token, "nonsolid")) flags |= Q3SURFACEPARM_NONSOLID; - else if (!strcmp(com_token, "origin")) + else if (!strcasecmp(com_token, "origin")) flags |= Q3SURFACEPARM_ORIGIN; - else if (!strcmp(com_token, "playerclip")) + else if (!strcasecmp(com_token, "playerclip")) flags |= Q3SURFACEPARM_PLAYERCLIP; - else if (!strcmp(com_token, "sky")) + else if (!strcasecmp(com_token, "sky")) flags |= Q3SURFACEPARM_SKY; - else if (!strcmp(com_token, "slick")) + else if (!strcasecmp(com_token, "slick")) flags |= Q3SURFACEPARM_SLICK; - else if (!strcmp(com_token, "slime")) + else if (!strcasecmp(com_token, "slime")) flags |= Q3SURFACEPARM_SLIME; - else if (!strcmp(com_token, "structural")) + else if (!strcasecmp(com_token, "structural")) flags |= Q3SURFACEPARM_STRUCTURAL; - else if (!strcmp(com_token, "trans")) + else if (!strcasecmp(com_token, "trans")) flags |= Q3SURFACEPARM_TRANS; - else if (!strcmp(com_token, "water")) + else if (!strcasecmp(com_token, "water")) flags |= Q3SURFACEPARM_WATER; else Con_Printf("%s parsing warning: unknown surfaceparm \"%s\"\n", search->filenames[i], com_token); - if (!COM_ParseToken(&text, true) || strcmp(com_token, "\n")) + if (!COM_ParseToken(&text, true) || strcasecmp(com_token, "\n")) { Con_Printf("%s parsing error: surfaceparm only takes one parameter.\n", search->filenames[i]); goto parseerror; @@ -3605,16 +3622,16 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l) else { // look for linebreak or } - while(COM_ParseToken(&text, true) && strcmp(com_token, "\n") && strcmp(com_token, "}")); + while(COM_ParseToken(&text, true) && strcasecmp(com_token, "\n") && strcasecmp(com_token, "}")); // break out to top level if it was } - if (!strcmp(com_token, "}")) + if (!strcasecmp(com_token, "}")) break; } } // add shader to list (shadername and flags) // actually here we just poke into the texture settings for (j = 0, out = loadmodel->brushq3.data_textures;j < loadmodel->brushq3.num_textures;j++, out++) - if (!strcmp(out->name, shadername)) + if (!strcasecmp(out->name, shadername)) out->surfaceparms = flags; } else @@ -3912,7 +3929,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) && out->type != Q3FACETYPE_MESH && out->type != Q3FACETYPE_FLARE) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type); + Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type); out->num_vertices = 0; out->num_triangles = 0; out->type = 0; // error @@ -3922,7 +3939,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) n = LittleLong(in->textureindex); if (n < 0 || n >= loadmodel->brushq3.num_textures) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures); + Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures); out->num_vertices = 0; out->num_triangles = 0; out->type = 0; // error @@ -3933,7 +3950,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) n = LittleLong(in->effectindex); if (n < -1 || n >= loadmodel->brushq3.num_effects) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects); + Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects); n = -1; } if (n == -1) @@ -3943,7 +3960,7 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l) n = LittleLong(in->lightmapindex); if (n < -1 || n >= loadmodel->brushq3.num_lightmaps) { - Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps); + Con_DPrintf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps); n = -1; } if (n == -1) @@ -4418,9 +4435,6 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l) q3dlightgrid_t *out; int count; - if (l->filelen == 0) - return; - in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name); @@ -4437,17 +4451,37 @@ static void Mod_Q3BSP_LoadLightGrid(lump_t *l) loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1; loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1; count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2]; - if (l->filelen < count * (int)sizeof(*in)) - Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)\n", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]); - if (l->filelen != count * (int)sizeof(*in)) - Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen); + if (l->filelen) + { + if (l->filelen < count * (int)sizeof(*in)) + Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)\n", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]); + if (l->filelen != count * (int)sizeof(*in)) + Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen); + } out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); loadmodel->brushq3.data_lightgrid = out; loadmodel->brushq3.num_lightgrid = count; // no swapping or validation necessary - memcpy(out, in, count * (int)sizeof(*out)); + if (l->filelen) + memcpy(out, in, count * (int)sizeof(*out)); + else + { + // no data, fill with white + int i; + for (i = 0;i < count;i++) + { + out[i].ambientrgb[0] = 128; + out[i].ambientrgb[1] = 128; + out[i].ambientrgb[2] = 128; + out[i].diffusergb[0] = 0; + out[i].diffusergb[1] = 0; + out[i].diffusergb[2] = 0; + out[i].diffusepitch = 0; + out[i].diffuseyaw = 0; + } + } Matrix4x4_CreateScale3(&loadmodel->brushq3.num_lightgrid_indexfromworld, loadmodel->brushq3.num_lightgrid_scale[0], loadmodel->brushq3.num_lightgrid_scale[1], loadmodel->brushq3.num_lightgrid_scale[2]); Matrix4x4_ConcatTranslate(&loadmodel->brushq3.num_lightgrid_indexfromworld, -loadmodel->brushq3.num_lightgrid_imins[0] * loadmodel->brushq3.num_lightgrid_cellsize[0], -loadmodel->brushq3.num_lightgrid_imins[1] * loadmodel->brushq3.num_lightgrid_cellsize[1], -loadmodel->brushq3.num_lightgrid_imins[2] * loadmodel->brushq3.num_lightgrid_cellsize[2]); @@ -4459,22 +4493,36 @@ static void Mod_Q3BSP_LoadPVS(lump_t *l) int totalchains; if (l->filelen == 0) + { + int i; + // unvised maps often have cluster indices even without pvs, so check + // leafs to find real number of clusters + loadmodel->brushq3.num_pvsclusters = 1; + for (i = 0;i < loadmodel->brushq3.num_leafs;i++) + loadmodel->brushq3.num_pvsclusters = min(loadmodel->brushq3.num_pvsclusters, loadmodel->brushq3.data_leafs[i].clusterindex + 1); + + // create clusters + loadmodel->brushq3.num_pvsclusterbytes = (loadmodel->brushq3.num_pvsclusters + 7) / 8; + totalchains = loadmodel->brushq3.num_pvsclusterbytes * loadmodel->brushq3.num_pvsclusters; + loadmodel->brushq3.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains); + memset(loadmodel->brushq3.data_pvsclusters, 0xFF, totalchains); return; + } in = (void *)(mod_base + l->fileofs); if (l->filelen < 9) Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name); loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters); - loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength); - if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8)) - Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters); - totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters; + loadmodel->brushq3.num_pvsclusterbytes = LittleLong(in->chainlength); + if (loadmodel->brushq3.num_pvsclusterbytes < ((loadmodel->brushq3.num_pvsclusters + 7) / 8)) + Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvsclusterbytes, loadmodel->brushq3.num_pvsclusters); + totalchains = loadmodel->brushq3.num_pvsclusterbytes * loadmodel->brushq3.num_pvsclusters; if (l->filelen < totalchains + (int)sizeof(*in)) - Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)\n", loadmodel->brushq3.num_pvsclusters, loadmodel->brushq3.num_pvschainlength, totalchains + sizeof(*in), l->filelen); + Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)\n", loadmodel->brushq3.num_pvsclusters, loadmodel->brushq3.num_pvsclusterbytes, totalchains + sizeof(*in), l->filelen); - loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains); - memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains); + loadmodel->brushq3.data_pvsclusters = Mem_Alloc(loadmodel->mempool, totalchains); + memcpy(loadmodel->brushq3.data_pvsclusters, (qbyte *)(in + 1), totalchains); } static void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius) @@ -4647,7 +4695,8 @@ static void Mod_Q3BSP_TraceLine_RecursiveBSPNode(trace_t *trace, q3mnode_t *node static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end, int markframe, const vec3_t segmentmins, const vec3_t segmentmaxs) { - int i, sides; + int i; + //int sides; float nodesegmentmins[3], nodesegmentmaxs[3]; q3mleaf_t *leaf; colbrushf_t *brush; @@ -4867,6 +4916,23 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *nod } } */ +#if 1 + for (;;) + { + nodesegmentmins[0] = max(segmentmins[0], node->mins[0]); + nodesegmentmins[1] = max(segmentmins[1], node->mins[1]); + nodesegmentmins[2] = max(segmentmins[2], node->mins[2]); + nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]); + nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]); + nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]); + if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2]) + return; + if (!node->plane) + break; + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + } +#elif 0 // FIXME: could be made faster by copying TraceLine code and making it use // box plane distances... (variant on the BoxOnPlaneSide code) for (;;) @@ -4885,11 +4951,69 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *nod { Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); node = node->children[1]; + continue; } else if (mod_q3bsp_debugtracebrush.integer == 1) { // recurse down node sides - sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane); + sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane); + if (sides == 3) + { + // segment box crosses plane + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + continue; + } + // take whichever side the segment box is on + node = node->children[sides - 1]; + continue; + } + else + { + // recurse down node sides + sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane); + if (sides == 3) + { + // segment box crosses plane + // now check start and end brush boxes to handle a lot of 'diagonal' cases more efficiently... + sides = BoxOnPlaneSide(thisbrush_start->mins, thisbrush_start->maxs, node->plane) | BoxOnPlaneSide(thisbrush_end->mins, thisbrush_end->maxs, node->plane); + if (sides == 3) + { + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + continue; + } + } + // take whichever side the segment box is on + node = node->children[sides - 1]; + continue; + } + return; + } +#else + // FIXME: could be made faster by copying TraceLine code and making it use + // box plane distances... (variant on the BoxOnPlaneSide code) + for (;;) + { + nodesegmentmins[0] = max(segmentmins[0], node->mins[0]); + nodesegmentmins[1] = max(segmentmins[1], node->mins[1]); + nodesegmentmins[2] = max(segmentmins[2], node->mins[2]); + nodesegmentmaxs[0] = min(segmentmaxs[0], node->maxs[0]); + nodesegmentmaxs[1] = min(segmentmaxs[1], node->maxs[1]); + nodesegmentmaxs[2] = min(segmentmaxs[2], node->maxs[2]); + if (nodesegmentmins[0] > nodesegmentmaxs[0] || nodesegmentmins[1] > nodesegmentmaxs[1] || nodesegmentmins[2] > nodesegmentmaxs[2]) + return; + if (!node->plane) + break; + if (mod_q3bsp_debugtracebrush.integer == 2) + { + Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end, markframe, segmentmins, segmentmaxs); + node = node->children[1]; + } + else if (mod_q3bsp_debugtracebrush.integer == 1) + { + // recurse down node sides + sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane); if (sides == 3) { // segment box crosses plane @@ -4905,7 +5029,7 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *nod else { // recurse down node sides - sides = BoxOnPlaneSide(segmentmins, segmentmaxs, node->plane); + sides = BoxOnPlaneSide(nodesegmentmins, nodesegmentmaxs, node->plane); if (sides == 3) { // segment box crosses plane @@ -4921,6 +5045,7 @@ static void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *nod node = node->children[sides - 1]; } } +#endif // hit a leaf leaf = (q3mleaf_t *)node; for (i = 0;i < leaf->numleafbrushes;i++) @@ -5027,32 +5152,58 @@ static void Mod_Q3BSP_TraceBox(model_t *model, int frame, trace_t *trace, const } } - -static int Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const q3mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) +static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) { - int clusterindex, side; - while (node->plane) + int clusterindex, side, nodestackindex = 0; + q3mnode_t *node, *nodestack[1024]; + node = model->brushq3.data_nodes; + if (!loadmodel->brushq3.num_pvsclusters) + return true; + for (;;) { - // node - recurse down the BSP tree - side = BoxOnPlaneSide(mins, maxs, node->plane) - 1; - if (side < 2) - node = node->children[side]; + if (node->plane) + { + // node - recurse down the BSP tree + side = BoxOnPlaneSide(mins, maxs, node->plane) - 1; + if (side < 2) + { + // box is on one side of plane, take that path + node = node->children[side]; + } + else + { + // box crosses plane, take one path and remember the other + nodestack[nodestackindex++] = node->children[0]; + node = node->children[1]; + } + } else { - // recurse for one child and loop for the other - if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs)) + // leaf - check cluster bit + clusterindex = ((q3mleaf_t *)node)->clusterindex; +#if 0 + if (clusterindex >= loadmodel->brushq3.num_pvsclusters) + { + Con_Printf("%i >= %i\n", clusterindex, loadmodel->brushq3.num_pvsclusters); return true; - node = node->children[1]; + } +#endif + if (CHECKPVSBIT(pvs, clusterindex)) + { + // it is visible, return immediately with the news + return true; + } + else + { + // nothing to see here, try another path we didn't take earlier + if (nodestackindex == 0) + break; + node = nodestack[--nodestackindex]; + } } } - // leaf - clusterindex = ((q3mleaf_t *)node)->clusterindex; - return pvs[clusterindex >> 3] & (1 << (clusterindex & 7)); -} - -static int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs) -{ - return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs); + // it is not visible + return false; } //Returns PVS data for a given point @@ -5065,20 +5216,16 @@ static qbyte *Mod_Q3BSP_GetPVS(model_t *model, const vec3_t p) while (node->plane) node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist]; if (((q3mleaf_t *)node)->clusterindex >= 0) - return model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength; + return model->brushq3.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvsclusterbytes; else return NULL; } static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, q3mnode_t *node) { - int i; - float d; - qbyte *pvs; - while (node->plane) { - d = PlaneDiff(org, node->plane); + float d = PlaneDiff(org, node->plane); if (d > radius) node = node->children[0]; else if (d < -radius) @@ -5090,25 +5237,23 @@ static void Mod_Q3BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, node = node->children[1]; } } - // if this is a leaf with a pvs, accumulate the pvs bits + // if this leaf is in a cluster, accumulate the pvs bits if (((q3mleaf_t *)node)->clusterindex >= 0) { - pvs = model->brushq3.data_pvschains + ((q3mleaf_t *)node)->clusterindex * model->brushq3.num_pvschainlength; + int i; + qbyte *pvs = model->brushq1.data_pvsclusters + ((q3mleaf_t *)node)->clusterindex * model->brushq1.num_pvsclusterbytes; for (i = 0;i < pvsbytes;i++) pvsbuffer[i] |= pvs[i]; } - else - memset(pvsbuffer, 0xFF, pvsbytes); - return; } //Calculates a PVS that is the inclusive or of all leafs within radius pixels //of the given point. static int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength) { - int bytes = model->brushq3.num_pvschainlength; + int bytes = model->brushq3.num_pvsclusterbytes; bytes = min(bytes, pvsbufferlength); - if (r_novis.integer) + if (r_novis.integer || !loadmodel->brushq3.num_pvsclusters) { memset(pvsbuffer, 0xFF, bytes); return bytes; @@ -5150,7 +5295,7 @@ static int Mod_Q3BSP_NativeContentsFromSuperContents(model_t *model, int superco extern void R_Q3BSP_DrawSky(struct entity_render_s *ent); extern void R_Q3BSP_Draw(struct entity_render_s *ent); extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius); -extern void R_Q3BSP_DrawLight(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz); +extern void R_Q3BSP_DrawLight(struct entity_render_s *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *lightcubemap); void Mod_Q3BSP_Load(model_t *mod, void *buffer) { int i, j;