From 8a47233bbf157cdd2819e68abe268fc6f8ebd1cf Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Thu, 10 Jan 2019 02:58:37 -0700 Subject: [PATCH] bsputil: start a --compare feature for helping with qbsp testing --- bsputil/bsputil.cc | 68 ++++++++++++++++++++++++- common/bsputils.cc | 101 ++++++++++++++++++++++++++++++++++--- include/common/bsputils.hh | 10 ++++ 3 files changed, 171 insertions(+), 8 deletions(-) diff --git a/bsputil/bsputil.cc b/bsputil/bsputil.cc index 389ef3ee..5fe199ae 100644 --- a/bsputil/bsputil.cc +++ b/bsputil/bsputil.cc @@ -32,6 +32,7 @@ #include #include #include // std::sort +#include /* FIXME - share header with qbsp, etc. */ typedef struct { @@ -453,6 +454,49 @@ CheckBSPFile(const mbsp_t *bsp) bsp->dmodels[0].maxs[2]); } +static void +CompareBSPFiles(const mbsp_t *refBsp, const mbsp_t *bsp) +{ + printf("comparing %d with %d faces\n", refBsp->numfaces, bsp->numfaces); + + const dmodel_t *world = BSP_GetWorldModel(bsp); + const dmodel_t *refWorld = BSP_GetWorldModel(refBsp); + + // iterate through the refBsp world faces + for (int i=0; inumfaces; i++) { + auto* refFace = BSP_GetFace(refBsp, refWorld->firstface + i); + qvec3f refFaceCentroid = Face_Centroid(refBsp, refFace); + + // FIXME: + vec3_t wantedPoint; + glm_to_vec3_t(refFaceCentroid, wantedPoint); + + vec3_t wantedNormal; + Face_Normal(refBsp, refFace, wantedNormal); + + // Search for a face in bsp touching refFaceCentroid. + auto* matchedFace = BSP_FindFaceAtPoint(bsp, world, wantedPoint, wantedNormal); + if (matchedFace == nullptr) { + printf("couldn't find a face at %f %f %f normal %f %f %f\n", + wantedPoint[0], wantedPoint[1], wantedPoint[2], + wantedNormal[0], wantedNormal[1], wantedNormal[2]); + } + + // TODO: run on some more complex maps +// auto* refFaceSelfCheck = BSP_FindFaceAtPoint(refBsp, refWorld, wantedPoint, wantedNormal); +// if (refFaceSelfCheck == refFace) { +// matches ++; +// } else { +// printf("not match at %f %f %f wanted %p got %p\n", wantedPoint[0], wantedPoint[1], wantedPoint[2], refFace, refFaceSelfCheck); +// Face_DebugPrint(refBsp, refFace); +// Face_DebugPrint(refBsp, refFaceSelfCheck); +// notmat++; +// } + } + + +} + int main(int argc, char **argv) { @@ -465,7 +509,7 @@ main(int argc, char **argv) printf("---- bsputil / ericw-tools " stringify(ERICWTOOLS_VERSION) " ----\n"); if (argc == 1) { printf("usage: bsputil [--extract-entities] [--extract-textures] [--convert bsp29|bsp2|bsp2rmq|q2bsp] [--check] [--modelinfo]" - "[--check] bspfile\n"); + "[--check] [--compare otherbsp] bspfile\n"); exit(1); } @@ -479,7 +523,27 @@ main(int argc, char **argv) ConvertBSPFormat(GENERIC_BSP, &bspdata); for (i = 0; i < argc - 1; i++) { - if (!strcmp(argv[i], "--convert")) { + if (!strcmp(argv[i], "--compare")) { + i++; + if (i == argc - 1) { + Error("--compare requires two arguments"); + } + // Load the reference BSP + + char refbspname[1024]; + bspdata_t refbspdata; + strcpy(refbspname, argv[i]); + DefaultExtension(refbspname, ".bsp"); + LoadBSPFile(refbspname, &refbspdata); + ConvertBSPFormat(GENERIC_BSP, &refbspdata); + + printf("comparing reference bsp %s with test bsp %s\n", refbspname, source); + + CompareBSPFiles(&refbspdata.data.mbsp, + &bspdata.data.mbsp); + + break; + } else if (!strcmp(argv[i], "--convert")) { i++; if (!(i < argc - 1)) { Error("--convert requires an argument"); diff --git a/common/bsputils.cc b/common/bsputils.cc index 36f7eada..1c95709b 100644 --- a/common/bsputils.cc +++ b/common/bsputils.cc @@ -47,6 +47,20 @@ const bsp2_dnode_t *BSP_GetNode(const mbsp_t *bsp, int nodenum) return &bsp->dnodes[nodenum]; } +const mleaf_t* BSP_GetLeaf(const mbsp_t *bsp, int leafnum) +{ + if (leafnum < 0 || leafnum >= bsp->numleafs) { + Error("Corrupt BSP: leaf %d is out of bounds (bsp->numleafs = %d)", leafnum, bsp->numleafs); + } + return &bsp->dleafs[leafnum]; +} + +const mleaf_t* BSP_GetLeafFromNodeNum(const mbsp_t *bsp, int nodenum) +{ + const int leafnum = (-1 - nodenum); + return BSP_GetLeaf(bsp, leafnum); +} + const dplane_t *BSP_GetPlane(const mbsp_t *bsp, int planenum) { Q_assert(planenum >= 0 && planenum < bsp->numplanes); @@ -248,12 +262,7 @@ vec_t Plane_Dist(const vec3_t point, const dplane_t *plane) static bool Light_PointInSolid_r(const mbsp_t *bsp, const int nodenum, const vec3_t point) { if (nodenum < 0) { - // FIXME: Factor out into bounds-checked getter - const int leafnum = (-1 - nodenum); - if (leafnum < 0 || leafnum >= bsp->numleafs) { - Error("Corrupt BSP: leaf %d is out of bounds (bsp->numleafs = %d)", leafnum, bsp->numleafs); - } - mleaf_t *leaf = &bsp->dleafs[leafnum]; + const mleaf_t *leaf = BSP_GetLeafFromNodeNum(bsp, nodenum); return (bsp->loadversion == Q2_BSPVERSION ? leaf->contents & Q2_CONTENTS_SOLID : (leaf->contents == CONTENTS_SOLID || leaf->contents == CONTENTS_SKY)); //mxd } @@ -282,6 +291,63 @@ bool Light_PointInWorld(const mbsp_t *bsp, const vec3_t point) return Light_PointInSolid(bsp, &bsp->dmodels[0], point); } +static const bsp2_dface_t *BSP_FindFaceAtPoint_r(const mbsp_t *bsp, const int nodenum, const vec3_t point, const vec3_t wantedNormal) +{ + if (nodenum < 0) { + // we're only interested in nodes, since faces are owned by nodes. + return nullptr; + } + + const bsp2_dnode_t *node = &bsp->dnodes[nodenum]; + const vec_t dist = Plane_Dist(point, &bsp->dplanes[node->planenum]); + + if (dist > 0.1) + return BSP_FindFaceAtPoint_r(bsp, node->children[0], point, wantedNormal); + if (dist < -0.1) + return BSP_FindFaceAtPoint_r(bsp, node->children[1], point, wantedNormal); + + // Point is close to this node plane. Check all faces on the plane. + for (int i=0; inumfaces; i++) { + const bsp2_dface_t *face = BSP_GetFace(bsp, node->firstface + i); + // First check if it's facing the right way + vec3_t faceNormal; + Face_Normal(bsp, face, faceNormal); + + if (DotProduct(faceNormal, wantedNormal) < 0) { + // Opposite, so not the right face. + continue; + } + + // Next test if it's within the boundaries of the face + plane_t *edgeplanes = Face_AllocInwardFacingEdgePlanes(bsp, face); + const bool insideFace = EdgePlanes_PointInside(face, edgeplanes, point); + free(edgeplanes); + + // Found a match? + if (insideFace) { + return face; + } + } + + // No match found on this plane. Check both sides of the tree. + const bsp2_dface_t *side0Match = BSP_FindFaceAtPoint_r(bsp, node->children[0], point, wantedNormal); + if (side0Match != nullptr) { + return side0Match; + } else { + return BSP_FindFaceAtPoint_r(bsp, node->children[1], point, wantedNormal); + } +} + +const bsp2_dface_t * BSP_FindFaceAtPoint(const mbsp_t *bsp, const dmodel_t *model, const vec3_t point, const vec3_t wantedNormal) +{ + return BSP_FindFaceAtPoint_r(bsp, model->headnode[0], point, wantedNormal); +} + +const bsp2_dface_t * BSP_FindFaceAtPoint_InWorld(const mbsp_t *bsp, const vec3_t point, const vec3_t wantedNormal) +{ + return BSP_FindFaceAtPoint(bsp, &bsp->dmodels[0], point, wantedNormal); +} + plane_t * Face_AllocInwardFacingEdgePlanes(const mbsp_t *bsp, const bsp2_dface_t *face) { @@ -360,3 +426,26 @@ qvec3f Face_Centroid(const mbsp_t *bsp, const bsp2_dface_t *face) // FIXME: GLM_PolyCentroid has a assertion that there are >= 3 points return GLM_PolyCentroid(GLM_FacePoints(bsp, face)); } + +void Face_DebugPrint(const mbsp_t *bsp, const bsp2_dface_t *face) +{ + const gtexinfo_t *tex = &bsp->texinfo[face->texinfo]; + const char *texname = Face_TextureName(bsp, face); + + logprint("face %d, texture '%s', %d edges...\n" + " vectors (%3.3f, %3.3f, %3.3f) (%3.3f)\n" + " (%3.3f, %3.3f, %3.3f) (%3.3f)\n", + (int)(face - bsp->dfaces), texname, face->numedges, + tex->vecs[0][0], tex->vecs[0][1], tex->vecs[0][2], tex->vecs[0][3], + tex->vecs[1][0], tex->vecs[1][1], tex->vecs[1][2], tex->vecs[1][3]); + + for (int i = 0; i < face->numedges; i++) { + int edge = bsp->dsurfedges[face->firstedge + i]; + int vert = Face_VertexAtIndex(bsp, face, i); + const vec_t *point = GetSurfaceVertexPoint(bsp, face, i); + logprint("%s %3d (%3.3f, %3.3f, %3.3f) :: edge %d\n", + i ? " " : " verts ", vert, + point[0], point[1], point[2], + edge); + } +} diff --git a/include/common/bsputils.hh b/include/common/bsputils.hh index 306b2243..18e32d69 100644 --- a/include/common/bsputils.hh +++ b/include/common/bsputils.hh @@ -31,6 +31,8 @@ int Face_GetNum(const mbsp_t *bsp, const bsp2_dface_t *f); // bounds-checked array access (assertion failure on out-of-bounds) const bsp2_dnode_t *BSP_GetNode(const mbsp_t *bsp, int nodenum); +const mleaf_t* BSP_GetLeaf(const mbsp_t *bsp, int leafnum); +const mleaf_t* BSP_GetLeafFromNodeNum(const mbsp_t *bsp, int nodenum); const dplane_t *BSP_GetPlane(const mbsp_t *bsp, int planenum); const bsp2_dface_t *BSP_GetFace(const mbsp_t *bsp, int fnum); bsp2_dface_t *BSP_GetFace(mbsp_t *bsp, int fnum); @@ -52,6 +54,13 @@ const dmodel_t *BSP_DModelForModelString(const mbsp_t *bsp, const std::string &s vec_t Plane_Dist(const vec3_t point, const dplane_t *plane); bool Light_PointInSolid(const mbsp_t *bsp, const dmodel_t *model, const vec3_t point); bool Light_PointInWorld(const mbsp_t *bsp, const vec3_t point); +/** + * Searches for a face touching a point and facing a certain way. + * Sometimes (water, sky?) there will be 2 overlapping candidates facing opposite ways, the provided normal + * is used to disambiguate these. + */ +const bsp2_dface_t *BSP_FindFaceAtPoint(const mbsp_t *bsp, const dmodel_t *model, const vec3_t point, const vec3_t wantedNormal); +const bsp2_dface_t *BSP_FindFaceAtPoint_InWorld(const mbsp_t *bsp, const vec3_t point, const vec3_t wantedNormal); plane_t *Face_AllocInwardFacingEdgePlanes(const mbsp_t *bsp, const bsp2_dface_t *face); bool EdgePlanes_PointInside(const bsp2_dface_t *face, const plane_t *edgeplanes, const vec3_t point); @@ -61,5 +70,6 @@ qvec3f Vertex_GetPos_E(const mbsp_t *bsp, int num); qvec3f Face_Normal_E(const mbsp_t *bsp, const bsp2_dface_t *f); std::vector GLM_FacePoints(const mbsp_t *bsp, const bsp2_dface_t *face); qvec3f Face_Centroid(const mbsp_t *bsp, const bsp2_dface_t *face); +void Face_DebugPrint(const mbsp_t *bsp, const bsp2_dface_t *face); #endif /* __COMMON_BSPUTILS_HH__ */